# Stone Teams Policy Enforcer - STEPER
# Andrew Sharrad 09/06/24 - andrew.sharrad@stonegroup.co.uk

#####################################################################################################
#                                                                                                   #
# Please see the associated manual for the full software license agreement, including consent       #
# around software updates and other changes                                                         #
#                                                                                                   #
# Summary:                                                                                          #
# - No Warranty, Implied or Otherwise is included with this software                                #
# - Use of this software is entirely at your own risk                                               #
# - The author and Stone Technologies shall not in any circumstances have any liability for any     #
#   losses or damages, howsoever caused                                                             #
# - By default, this software includes AutoUpdate functionality. By using this software, you        #
#   consent to the software updating itself or related files, or disabling itself as required       #
# - For instructions, including details on how to disable AutoUpdate, see the manual                #
#                                                                                                   #
#####################################################################################################


#####################################################################################################
#                                                                                                   #
# Please do not modify this script. If you have any issues, please contact support@stonegroup.co.uk #
#                                                                                                   #
#####################################################################################################

<#

.SYNOPSIS
This can be used to Apply Teams Policies (or Packages) and Licensing (via licensing groups) to Teachers or Students
It can also update Security Group, Unified Group and Distribution Group memberships
It is designed to be used in conjunction an ongoing Sync to M365 using School Data Sync and the operator's MIS system,
Or it can be used completely stand-alone

.DESCRIPTION
Loads a configuration INI file, connects to the cloud and updates the necessary users
Must be run with elevated rights especially if run as part of a scheduled task.
The script is designed to run as required or with a scheduled task 
Logging: Examine the Detailed Log File, Application Event Log and Registry at HKLM:\SOFTWARE\Stone\M365\STEPER\ProfileName

.EXAMPLE
.\steper.ps1
.\steper.ps1 -MakeChanges
.\steper.ps1 -SettingsFile C:\_Stone\MySettings.INI
.\steper.ps1 -SettingsFile C:\_Stone\MySettings.INI -MakeChanges
.\steper.ps1 -SettingsFile C:\_Stone\MySettings.INI -WhatIf
.\steper.ps1 -UpdateModules
.\steper.ps1 -Verbose

.PARAMETER SettingsFile
STEPER needs a settings configuration file. If you dont give the name, it will look for STEPER.INI

.PARAMETER MakeChanges
STEPER supports Simulation only mode, where STEPER will show the changes that would be made to users, without actually making them.
Use this feature to perform proof of concept testing to make sure that STEPER will make the changes you need.
When you are ready to actually make changes, either set MakeChanges=True in the Settings File or use -MakeChanges option on the command line
to turn off Simulation only mode
Using -MakeChanges on the command line will overwrite MakeChanges=False in the settings configuration file

.PARAMETER WhatIf
Ensures Simulation only mode is always used, even if MakeChanges=True is set in the settings file or -MakeChanges used on the command line

.PARAMETER UpdateModules
Forces all Powershell modules to be updated

.PARAMETER Verbose
Displays additional onscreen messages during processing and also writes to the system event log

.PARAMETER ApplytoAllTeachers
Override - Applies all applicable changes to all Teachers, instead of just those that weren't in the Teacher licensing group

.PARAMETER ApplytoAllStudents
Override - Applies all applicable changes to all Students, instead of just those that weren't in the Students licensing group

.PARAMETER ApplytoAll
Override - Applies all applicable changes to all Users (Teachers and Students), instead of just those that weren't in the licensing groups

.NOTES
This software is provided without any warranties or guarantees, implied or otherwise. Usage is entirely at the operator's own risk
Contact Andrew Sharrad at Stone Technologies Support for further help

.LINK
https://kb.stonegroup.co.uk/index.php?View=entry&EntryID=796

#>

param ([string]$SettingsFile, [switch]$MakeChanges = $false, [switch]$UpdateModules = $false, [switch]$Verbose = $false, [switch]$ApplytoAllTeachers = $false, [switch]$ApplytoAllStudents = $false, [switch]$ApplytoAll = $false, [switch]$WhatIf=$false)

if ($ApplytoAll -eq $true) {
	$ApplytoAllTeachers = $true
	$ApplytoAllStudents = $true
}

$disableelevate = $false
if ($SettingsFile -or $MakeChanges -eq $true -or $UpdateModules -eq $true -or $Verbose -eq $true -or $ApplytoAllTeachers -eq $true -or $ApplytoAllStudents -eq $true -or $ApplytoAll -eq $true) {
    $disableelevate = $true
}

$starttime = (Get-Date -format "dd/MM/yyyy HH:mm:ss")
$datenow = (Get-Date).tostring("dd-MM-yyyy HHmmss")
$username = $env:UserName

$hostname = hostname

$EmailAvailable = $false
$DontRecordCompletionTime = $false
$UseCSVforUserDataInLogFile = $false

$ProgramName = "STEPER"
$Caption = "Stone Teams Policy Enforcer"

$DefaultSettingsFileName = "STEPER.INI"
$ProcessingSettings=$false

[int]$VersionMajor = 1
[int]$VersionMinor = 0
[int]$VersionTrivial = 99
$SettingsFileVersion = "118"

[int]$Channel = 1000
[int]$PatchLevel=100 # default

$Version = ( "v" + $VersionMajor + "." + $versionminor + "." + $VersionTrivial)

$UpdatesAvailable = @()
$monthlyupdatecheck = $false
$IgnoreCollectionIDUserType = $false

$Date = "14/12/23"
$Author = "Andrew Sharrad"
$AppLogName = "STEPER"

[int]$AppLogCategory = 2
[int]$AppLogBaseIndex = 5200 # the base event ID for the application log
[int]$MaxSettingsGroups = 100

$CopySettings = $null
$LongJobUpdateInterval = 100

$Trimchar=' ,' + [char]0x009
# Also see the function NumericalListCreator

<#
AppLog Categories
0 None
1 Devices
2 Disk
3 Printers
4 Services
5 Shell
6 System Event
7 Network
#>

# Record Problems
$MajorErrors = @()
$Warnings = @()
$Information = @()

# Record what we've done
$Actions = @()

#Record Settings Information
$Credentials = @()
$Tenancy = @()
$SourceGroups = @()
$LicensingInformation = @()
$PoliciesToProcess = @()
$PackagesToProcess = @()
$SchoolInformation = @()
$PolicyInformation = @()
$UnifiedGroups = @()
$FilteredUnifiedGroupsToProcess = @()
$FilteredSecurityGroupsToProcess = @()
$FilteredDistributionGroupsToProcess = @()
$Department = @()
$EmailInfo = @()
$OtherSettings = @()
$PackageBatchesSubmitted = @()
$PolicyBatchesSubmitted = @()

#Next Run Overrides
$ApplytoAllTeachersOnNextRun=$false
$ApplytoAllStudentsOnNextRun=$false
$ApplytoAllonNextRunSummary = @()
[int]$ApplytoAllTeachersOnNextRunThisFunc=0
[int]$ApplytoAllStudentsOnNextRunThisFunc=0

#Final Results
$Results = @()

$Global:ProgressPreference = 'SilentlyContinue'
$ErrorActionPreference= 'silentlycontinue'
$WarningPreference = 'SilentlyContinue'
$DebugPreference  = 'SilentlyContinue' # 1.0.61

# Registry Library 1.01 05/4/19 Andrew Sharrad

function Test-RegistryValue {

param (

[parameter(Mandatory=$true)]
[ValidateNotNullOrEmpty()]$Path,

[parameter(Mandatory=$false)]
[ValidateNotNullOrEmpty()]$Name,
 
[parameter(Mandatory=$false)]
[ValidateNotNullOrEmpty()]$ValueType,
 
[switch]$DeleteIfIncorrectType = $false
 
)

if ((Test-Path $Path) -eq $false) { return $false }

if ($Name -ne $null) {
    $Key = Get-Item -LiteralPath $Path
    if ($Key.GetValue($Name, $null) -ne $null) {
        if ($ValueType) {
            if ($key.GetValueKind($Name) -eq $ValueType) {
                return $true
                } else {
                if ($DeleteIfIncorrectType -eq $true) { Remove-ItemProperty -Path $Path -Name $Name }
                return $false
                }
            } else {
            return $true
            }
        } else {
        return $false
        }
    } else {
    return $true
    }
}




function Set-RegistryTree {

param (
[parameter(Mandatory=$true)]
[ValidateNotNullOrEmpty()]$Path
)

$Splits=$Path.Split("\")
$Tree=$Splits[0]+'\'

For ($i=1; $i -lt $Splits.Count; $i++)
	{
	$Tree = $Tree + '\' + $Splits[$i]
	If ((Test-RegistryValue -Path $Tree) -eq $false)
		{
		New-Item -Path $Tree -ErrorAction SilentlyContinue | Out-Null
		}
	}
}


function Set-RegistryValue {

param (

[parameter(Mandatory=$true)]
[ValidateNotNullOrEmpty()]$Path,

[parameter(Mandatory=$true)]
[ValidateNotNullOrEmpty()]$Name,

[parameter(Mandatory=$true)]
[ValidateNotNullOrEmpty()]$Value,
 
[parameter(Mandatory=$true)]
[ValidateNotNullOrEmpty()]$ValueType,
 
[switch]$Verify = $false
 
)

Set-RegistryTree -Path $Path
New-ItemProperty -Path $Path -Name $Name -Value $Value -PropertyType $ValueType -Force -ErrorAction SilentlyContinue | out-null

if ($Verify -eq $true) {
	if ( (Get-RegistryValue -Path $Path -Name $Name -ValueType $ValueType) -eq $Value) {
		return $true
		} else {
		return $false
		}
		
	} else {
	return $null
	}
}

function GetOrSet-RegistryValue {

param (

[parameter(Mandatory=$true)]
[ValidateNotNullOrEmpty()]$Path,

[parameter(Mandatory=$true)]
[ValidateNotNullOrEmpty()]$Name,

[parameter(Mandatory=$true)]
[ValidateNotNullOrEmpty()]$Value,
 
[parameter(Mandatory=$true)]
[ValidateNotNullOrEmpty()]$ValueType,
 
[switch]$Verify = $false
 
)

If ((Test-RegistryValue -Path $Path -Name $Name -ValueType $ValueType -DeleteIfIncorrectType) -eq $false)
	{
	if ($Verify -eq $true) {
		if ((Set-RegistryValue -Path $Path -Name $Name -ValueType $ValueType -Value $Value -Verify) -eq $false) {
			return $null
			} else {
			return $Value
			}
		} else {
		Set-RegistryValue -Path $Path -Name $Name -ValueType $ValueType -Value $Value
		Return $Value
		}
	
	} else {
	$Key = Get-Item -LiteralPath $Path
    Return($Key.GetValue($Name, $null))
	}
}

function Get-RegistryValue {
param (

[parameter(Mandatory=$true)]
[ValidateNotNullOrEmpty()]$Path,

[parameter(Mandatory=$true)]
[ValidateNotNullOrEmpty()]$Name,
 
[parameter(Mandatory=$false)]
[ValidateNotNullOrEmpty()]$ValueType
 
)

if ($ValueType) {
	If ((Test-RegistryValue -Path $Path -Name $Name -ValueType $ValueType) -eq $false)
		{
		Return $null
		} else {
	
		$Key = Get-Item -LiteralPath $Path
		Return($Key.GetValue($Name, $null))
		}
	} else {
	If ((Test-RegistryValue -Path $Path -Name $Name) -eq $false)
		{
		Return $null
		} else {
	
		$Key = Get-Item -LiteralPath $Path
		Return($Key.GetValue($Name, $null))
		}	
	
	}
}


function Get-RegistryType  {
param (

[parameter(Mandatory=$true)]
[ValidateNotNullOrEmpty()]$Path,

[parameter(Mandatory=$true)]
[ValidateNotNullOrEmpty()]$Name
 
)

If ((Test-RegistryValue -Path $Path -Name $Name) -eq $false)
	{
	Return $null
	} else {
	$Key = Get-Item -LiteralPath $Path
	Return($key.GetValueKind($Name))
	}
} 



function Get-IniFile 
{  
    param(  
        [parameter(Mandatory = $true)] [string] $filePath  
    )  
	$INIFileOK=$true
    $anonymous = "NoSection"

    $ini = @{}  
    switch -regex -file $filePath  
    {  
        "^\[(.+)\]$" # Section  
        {  
            $section = $matches[1]  
            if ( $ini[$section].count -gt 0) { $INIFileOK = $false }
			$ini[$section] = @{}  
            $CommentCount = 0  
        }  

        "^(;.*)$" # Comment  
        {  
            if (!($section))  
            {  
                $section = $anonymous  
                $ini[$section] = @{}  
            }  
            $value = $matches[1]  
            $CommentCount = $CommentCount + 1  
            $name = "Comment" + $CommentCount  
            #$ini[$section][$name] = $value
			if ($value.length -gt 0) {
                $ini[$section][$name] = $value  
            } else {
                $ini[$section][$name] = $null
            }
        }   

        "(.+?)\s*=\s*(.*)" # Key  
        {  
            if (!($section))  
            {  
                $section = $anonymous  
                $ini[$section] = @{}  
            }  
            $name,$value = $matches[1..2]  
            #$ini[$section][$name] = $value
            if ($value.length -gt 0) {
                $ini[$section][$name] = $value  
            } else {
                $ini[$section][$name] = $null
            }			
        }  
    }  

    return $ini, $INIFileOK
}  


Function Get-StringHash([String] $String,$HashName = "MD5") 
{ 
$StringBuilder = New-Object System.Text.StringBuilder 
[System.Security.Cryptography.HashAlgorithm]::Create($HashName).ComputeHash([System.Text.Encoding]::UTF8.GetBytes($String))|%{ 
[Void]$StringBuilder.Append($_.ToString("x2")) 
} 
$StringBuilder.ToString() 
}

function Test-Filter
{
param ($CodeBlock,$Local)
    if ($Local -eq $true -and $CodeBlock -notlike '*$_.*') {
        return $false
    }
    if ($Local -eq $false -and $CodeBlock -like '*$_.*') {
        return $false
    }
    $Errors = @()
    [void][System.Management.Automation.Language.Parser]::ParseInput($CodeBlock,[ref]$null,[ref]$Errors)
    if($Errors.Count -gt 0){
	    return $false
    } else {
        return $true
    }									
}

function Test-IsGuid
{
    [OutputType([bool])]
    param
    (
        [Parameter(Mandatory = $true)]
        [string]$ObjectGuid
    )

    # Define verification regex
    [regex]$guidRegex = '(?im)^[{(]?[0-9A-F]{8}[-]?(?:[0-9A-F]{4}[-]?){3}[0-9A-F]{12}[)}]?$'

    # Check guid against regex
    return $ObjectGuid -match $guidRegex
}

#$argcmd = $script:MyInvocation.Line
#$program = $script:MyInvocation.InvocationName

#$script:MyInvocation | fl *

#$argstocall =''
#$argstocall = $argcmd.ToString().Replace($program,'')
#[string[]]$argstocall = $argstocall.Trim()

$myWindowsID=[System.Security.Principal.WindowsIdentity]::GetCurrent()
$myWindowsPrincipal=new-object System.Security.Principal.WindowsPrincipal($myWindowsID)
 
# Get the security principal for the Administrator role
$adminRole=[System.Security.Principal.WindowsBuiltInRole]::Administrator
# Check to see if we are currently running "as Administrator"

if ($myWindowsPrincipal.IsInRole($adminRole))
    {
    # We are running "as Administrator" - so change the title and background color to indicate this
    $Host.UI.RawUI.WindowTitle = $myInvocation.MyCommand.Definition + " " + $ProgramName + " " + $Version + " (Elevated)"
} else {
    write-Host "Stone Teams Policy Enforcer (STEPER): $Version $Author $Date" -foreground green
    if ($disableelevate -eq $true) {
        Write-Host "Administrator rights are not present - please run STEPER with elevated Administrator rights" -Foreground cyan
        start-sleep -Seconds 3
        exit 1
        }
    
    Write-Host "Administrator rights are not present - spawning a second copy of STEPER with elevated Administrator rights" -Foreground cyan
    
    $newProcess = new-object System.Diagnostics.ProcessStartInfo "PowerShell";
    $newProcess.Arguments = "& '" + $script:MyInvocation.MyCommand.Path + "'" 
    $newProcess.Verb = "runas";
    try {
        $null = [System.Diagnostics.Process]::Start($newProcess);
    }
    catch {
        $MajorErrors += "- Not running with Administrative Privileges"
        write-host "Please run $ProgramName with Administrative Privileges"
        start-sleep -Seconds 3
    }
	exit 1
}

if ($MajorErrors.Count -gt 0) { exit $MajorErrors.Count } # error

[console]::BackgroundColor = "black"
cls

[int]$screenwidth=$Host.UI.RawUI.Buffersize.Width

if ($screenwidth -lt 30 -or $screenwidth -gt 560) {
	$screenwidth = 30
}

$screenstring = ""
for ($a=1; $a -lt $screenwidth; $a++) {
	$screenstring = "=$screenstring"
}

$regalt = "HKLM:\SOFTWARE\Stone\M365\$ProgramName"
if ((Get-RegistryValue -Path "$regalt\Versions\$version" -Name 'PatchLevel' -ValueType DWORD ) -ne $null) {
	[int]$PatchLevel = Get-RegistryValue -Path "$regalt\Versions\$version" -Name 'PatchLevel' -ValueType DWORD
}

write-Host "Stone Teams Policy Enforcer (STEPER): $Version (C:$($Channel):$PatchLevel) $Author $Date" -foreground green
write-host
write-Host "Please see the license agreement in the accompanying manual and the notice regarding receiving updates" -foreground white
write-Host "No Warranty, Implied or Otherwise is supplied with this software" -foreground white
write-host

Set-RegistryValue -Path "$regalt\Global Information" -Name 'Version' -ValueType String -Value "Stone Teams Policy Enforcer (STEPER): $Version (C:$($Channel):$PatchLevel) $Author $Date"

$dir = split-path -parent $MyInvocation.MyCommand.Definition

if ($dir[0] -eq '\') {
	Write-Host "PROCESS FAILURE: STEPER must be run from a local drive. It does not support UNC Paths" -Foreground Magenta
    exit 1
}

if ($psISE -ne $null) {
	Write-Host "PROCESS FAILURE: STEPER does not support being run from inside the PowerShell ISE" -Foreground Magenta
    exit 1
}

write-host "$screenstring" -foregroundcolor green 
write-host

if ($MyInvocation.UnBoundArguments) {
	write-host "$($MyInvocation.UnBoundArguments.count) Unrecognised or Unbound Command Line Arguments found" -foregroundcolor Red
	write-host
	$Warnings += "- $($MyInvocation.UnBoundArguments.count) Unrecognised or Unbound Command Line Arguments found"
	write-host "$screenstring" -foregroundcolor green 
	write-host
}

	
$SettingsSearchLocations = @()

if ($SettingsFile) {
    $SettingsFile = $SettingsFile.ToUpper()

	if ($SettingsFile -like "*.ini") {
		$SettingsSearchLocations+= $SettingsFile
	} else {
		$SettingsSearchLocations+= "$SettingsFile.INI"
	}

	if ( (Split-Path $SettingsFile -parent) -eq '') {
		
		if ($dir) {
			if ($dir[$dir.Length-1] -eq '\') {
				if ($SettingsFile -like "*.ini") {
					$SettingsSearchLocations+= "$dir$SettingsFile"
				} else {
					$SettingsSearchLocations+= "$dir$SettingsFile.INI"
				}
			} else {
				if ($SettingsFile -like "*.ini") {
					$SettingsSearchLocations+= "$dir\$SettingsFile"
				} else {
					$SettingsSearchLocations+= "$dir\$SettingsFile.INI"
				}
			}
		} else {

			$dir = $null
			$dir += (get-location).Path

			if ($dir) {
				if ($dir[$dir.Length-1] -eq '\') {
					if ($SettingsFile -like "*.ini") {
						$SettingsSearchLocations+= "$dir$SettingsFile"
					} else {
						$SettingsSearchLocations+= "$dir$SettingsFile.INI"
					}
				} else {
					if ($SettingsFile -like "*.ini") {
						$SettingsSearchLocations+= "$dir\$SettingsFile"
					} else {
						$SettingsSearchLocations+= "$dir\$SettingsFile.INI"
					}
				}
			}
		}
	}
	
	
} else {
    #$dir = split-path -parent $MyInvocation.MyCommand.Definition
    if ($dir) {
    	if ($dir[$dir.Length-1] -eq '\') {
		    $SettingsSearchLocations += "$dir$DefaultSettingsFileName"
	    } else {
    		$SettingsSearchLocations += "$dir\$DefaultSettingsFileName"
    	}
    }

    $dir = $null
    $dir += (get-location).Path

    if ($dir) {
    	if ($dir[$dir.Length-1] -eq '\') {
		    $SettingsSearchLocations += "$dir$DefaultSettingsFileName"
	    } else {
    		$SettingsSearchLocations += "$dir\$DefaultSettingsFileName"
    	}
    }
    $SettingsSearchLocations += "$DefaultSettingsFileName"
}


$MajorError = $true

foreach ($Location in $SettingsSearchLocations) {

	if (Test-Path "$Location" -PathType leaf ){
		$SettingsFileName = $Location
		$MajorError = $false
		break
	}
}

if ($MajorError -eq $true) {
	$MajorErrors += "- Unable to Find the Settings File in one of the following locations: $SettingsSearchLocations"
    write-Host "Unable to Find the Settings File in one of the following locations:" -foreground Red
	foreach ($line in $SettingsSearchLocations) {
        write-Host "$line" -foreground yellow
    }
}

# check for key settings to be added to old version of settings files
# adding these settings in helps highlight awareness for the user

[int]$inifileupgraded = 0
$FoundDisableAutoUpdate = $false
$FoundRepeatLogEventThreshold = $false
$FoundRemediationSection = $false
$FoundOverrideSection = $false
$FoundUseCSVforUserDataInLogFile = $false
$FoundLinesToRemove = $false
$FoundMaxBatchSize = $false
$FoundUseLocalAttributeFiltering = $false
$FoundUseAzureADforLocalAttributeFilteringByDefault = $false
$FoundGetExtraStatistics = $false
$FoundProtectPassword = $false
$FoundGlobalOnFailureApplytoAllUsersOnNextRun = $false
$FoundFunctionControl = $false
$FoundCollections = $false
$FoundGetPackageInformation = $false
$FoundRefreshTeacherLicensingStatusAfterLicensingGroupChanges = $false
$FoundRefreshStudentLicensingStatusAfterLicensingGroupChanges = $false

if ($MajorErrors.Count -eq 0) {

	#get inifilecontents for recording later
	$inifilecontents = Get-Content $SettingsFileName

	if ($inifilecontents) {
		
		$inifilewritetime = (Get-ChildItem $SettingsFileName).LastWriteTime
		$inifilewritetime = $inifilewritetime.ToString("dd-MM-yyyy HH:mm:ss")
		
		if ( @($inifilecontents | where {$_ -eq [string]'[SourceGroups]'}).count -gt 0) {
			write-Host "Performing a major settings file upgrade - upgrading [SourceGroups] to [TeacherSourceGroups] and [StudentSourceGroups]" -foreground yellow

			$iniFile = $null
			$iniFile = @()
			$iniFile, $INIFileOK = Get-IniFile $SettingsFileName

			$ComplexSettingsToProcesses = @([pscustomobject]@{Usertype = 'Teacher'; OldSetting= 'AllTeachersSourceGroupName'; NewSetting= 'RequiredOrGroups'; AltNewSetting = 'RequiredOrRecursiveGroups'; AltSettingSource = 'EnableAllTeachersSourceGroupRecursion'; DontAdd = $true},
			[pscustomobject]@{Usertype = 'Student'; OldSetting= 'AllStudentsSourceGroupName'; NewSetting= 'RequiredOrGroups'; AltNewSetting = 'RequiredOrRecursiveGroups'; AltSettingSource = 'EnableAllStudentsSourceGroupRecursion'; DontAdd = $true},
			[pscustomobject]@{Usertype = 'Teacher'; OldSetting= 'ExcludedTeachersGroupName'; NewSetting= 'NotRequiredGroups'; AltNewSetting = 'NotRequiredRecursiveGroups'; AltSettingSource = 'EnableExcludedTeachersGroupRecursion'; DontAdd = $true},
			[pscustomobject]@{Usertype = 'Student'; OldSetting= 'ExcludedStudentsGroupName'; NewSetting= 'NotRequiredGroups'; AltNewSetting = 'NotRequiredRecursiveGroups'; AltSettingSource = 'EnableExcludedStudentsGroupRecursion'; DontAdd = $true}
			[pscustomobject]@{Usertype = 'Teacher'; OldSetting= 'AllTeachersDomainFilter'; NewSetting= $null; AltNewSetting = 'DomainFilter'; AltSettingSource = 'FilterAllTeachersBasedonEmailDomain'; DontAdd = $true},
			[pscustomobject]@{Usertype = 'Student'; OldSetting= 'AllStudentsDomainFilter'; NewSetting= $null; AltNewSetting = 'DomainFilter'; AltSettingSource = 'FilterAllStudentsBasedonEmailDomain'; DontAdd = $true})

			$BasicSettingsToProcesses = @([pscustomobject]@{Usertype = 'Teacher'; OldSetting= 'DontProcessStudentsFoundinTeachersSourceGroup'; NewSetting= 'DontProcessStudentsFoundinTeacherSourceGroups'; DefaultValue = $false; DontAdd = $true},			
			[pscustomobject]@{Usertype = 'Teacher'; OldSetting= 'DisableTeacherGroupNameorIDChecking'; NewSetting= 'DisableNameorIDCheckingForGroupRules'; DefaultValue = $false; DontAdd = $true},
			[pscustomobject]@{Usertype = 'Teacher'; OldSetting= $null; NewSetting= 'IncludeDistributionGroupsInGroupRules'; DefaultValue = $true; DontAdd = $true},
			[pscustomobject]@{Usertype = 'Teacher'; OldSetting= $null; NewSetting= 'DontAllowMissingNotRequiredGroups'; DefaultValue = $false; DontAdd = $true},
			[pscustomobject]@{Usertype = 'Teacher'; OldSetting= 'LogAvailableTeachers'; NewSetting= 'LogAvailableUsers'; DefaultValue = $false; DontAdd = $true},
			[pscustomobject]@{Usertype = 'Teacher'; OldSetting= $null; NewSetting= 'RequiredGroups'; DefaultValue = $null; DontAdd = $true},
			[pscustomobject]@{Usertype = 'Teacher'; OldSetting= $null; NewSetting= 'RequiredRecursiveGroups'; DefaultValue = $null; DontAdd = $true},
			[pscustomobject]@{Usertype = 'Teacher'; OldSetting= $null; NewSetting= 'RequiredOrGroups'; DefaultValue = $null; DontAdd = $true},
			[pscustomobject]@{Usertype = 'Teacher'; OldSetting= $null; NewSetting= 'RequiredOrRecursiveGroups'; DefaultValue = $null; DontAdd = $true},
			[pscustomobject]@{Usertype = 'Teacher'; OldSetting= $null; NewSetting= 'NotRequiredGroups'; DefaultValue = $null; DontAdd = $true},
			[pscustomobject]@{Usertype = 'Teacher'; OldSetting= $null; NewSetting= 'NotRequiredRecursiveGroups'; DefaultValue = $null; DontAdd = $true},
			[pscustomobject]@{Usertype = 'Student'; OldSetting= 'DontProcessTeachersFoundinStudentsSourceGroup'; NewSetting= 'DontProcessTeachersFoundinStudentSourceGroups'; DefaultValue = $false; DontAdd = $true},
			[pscustomobject]@{Usertype = 'Student'; OldSetting= 'DisableStudentGroupNameorIDChecking'; NewSetting= 'DisableNameorIDCheckingForGroupRules'; DefaultValue = $false; DontAdd = $true},
			[pscustomobject]@{Usertype = 'Student'; OldSetting= $null; NewSetting= 'IncludeDistributionGroupsInGroupRules'; DefaultValue = $true; DontAdd = $true},
			[pscustomobject]@{Usertype = 'Student'; OldSetting= $null; NewSetting= 'DontAllowMissingNotRequiredGroups'; DefaultValue = $false; DontAdd = $true},
			[pscustomobject]@{Usertype = 'Student'; OldSetting= 'LogAvailableStudents'; NewSetting= 'LogAvailableUsers'; DefaultValue = $false; DontAdd = $true},
			[pscustomobject]@{Usertype = 'Student'; OldSetting= $null; NewSetting= 'RequiredGroups'; DefaultValue = $null; DontAdd = $true},
			[pscustomobject]@{Usertype = 'Student'; OldSetting= $null; NewSetting= 'RequiredRecursiveGroups'; DefaultValue = $null; DontAdd = $true},
			[pscustomobject]@{Usertype = 'Student'; OldSetting= $null; NewSetting= 'RequiredOrGroups'; DefaultValue = $null; DontAdd = $true},
			[pscustomobject]@{Usertype = 'Student'; OldSetting= $null; NewSetting= 'RequiredOrRecursiveGroups'; DefaultValue = $null; DontAdd = $true},
			[pscustomobject]@{Usertype = 'Student'; OldSetting= $null; NewSetting= 'NotRequiredGroups'; DefaultValue = $null; DontAdd = $true},
			[pscustomobject]@{Usertype = 'Student'; OldSetting= $null; NewSetting= 'NotRequiredRecursiveGroups'; DefaultValue = $null; DontAdd = $true}
			)

			$SourceGroupSettings = $null
			$SourceGroupSettings = @{}
			$SourceGroupSettings["Teacher"] = @{} 
			$SourceGroupSettings["Student"] = @{} 

			foreach ($Setting in $ComplexSettingsToProcesses) {
				if ($Setting.NewSetting -ne $null) {
					$string = $null
					if ($INIFileOK -eq $true) {
						$string = $inifile["$($setting.Usertype)SourceGroups"].($Setting.NewSetting)
					}
					if ($string -ne $null) {
						#$string = $string.Replace("$($Setting.NewSetting)=",'').Trim()
						$SourceGroupSettings[$Setting.UserType].($Setting.NewSetting) = $string
						#$inifilecontents = @($inifilecontents | where {$_ -notlike "$($Setting.NewSetting)=*" })
					}
				}
			}

			foreach ($Setting in $BasicSettingsToProcesses) {
				$string = $null
				if ($INIFileOK -eq $true) {
					$string = $inifile["$($setting.Usertype)SourceGroups"].($Setting.NewSetting)
				}
				if ($string -ne $null) {
					#$string = $string.Replace("$($Setting.NewSetting)=",'').Trim()
					$SourceGroupSettings[$Setting.UserType].($Setting.NewSetting) = $string
					#$inifilecontents = @($inifilecontents | where {$_ -notlike "$($Setting.NewSetting)=*" })
				}				
			}

			foreach ($Setting in $ComplexSettingsToProcesses) {
				$string = $null
				$string = ($inifilecontents | where {$_ -like "$($Setting.OldSetting)=*"} | Select -First 1)
				if ($string -ne $null) {
					$string = $string.Replace("$($Setting.OldSetting)=",'').Trim()
					if ($Setting.AltSettingSource -ne $null) {
						$Temp = $null
						$Temp = ($inifilecontents | where {$_ -like "$($Setting.AltSettingSource)=*"} | Select -First 1)
						if ($temp -ne $null) {
							$Temp = $Temp.Replace("$($Setting.AltSettingSource)=",'').Trim()
						}
						if ( ($Temp -eq $true -or $Temp -eq 1) -and $Setting.AltSettingSource -ne $null) {
							if ($Setting.AltNewSetting -ne $null) {
								if ($SourceGroupSettings[$Setting.UserType].($Setting.AltNewSetting) -and $Setting.DontAdd -eq $false) {
									$SourceGroupSettings[$Setting.UserType].($Setting.AltNewSetting) = $SourceGroupSettings[$Setting.UserType].($Setting.AltNewSetting) + "," + $string
								} else {
									$SourceGroupSettings[$Setting.UserType].($Setting.AltNewSetting) = $string
								}
							}
						} else {
							if ($Setting.NewSetting -ne $null) {
								if ($SourceGroupSettings[$Setting.UserType].($Setting.NewSetting) -and $Setting.DontAdd -eq $false) {
									$SourceGroupSettings[$Setting.UserType].($Setting.NewSetting) = $SourceGroupSettings[$Setting.UserType].($Setting.NewSetting) + "," + $string
								} else {
									$SourceGroupSettings[$Setting.UserType].($Setting.NewSetting) = $string
								}
							}
						}
					}
				}
				#$inifilecontents = @($inifilecontents | where {$_ -notlike "$($Setting.OldSetting)=*" })
			}

			foreach ($Setting in $BasicSettingsToProcesses) {
				if ($Setting.OldSetting -ne $null) {
					$string = $null
					$string = ($inifilecontents | where {$_ -like "$($Setting.OldSetting)=*"} | Select -First 1)
					if ($string -ne $null) {
						$String = $String.Replace("$($Setting.OldSetting)=",'').Trim()
						if ($SourceGroupSettings[$Setting.UserType].($Setting.NewSetting) -and $Setting.DontAdd -eq $false) {
							$SourceGroupSettings[$Setting.UserType].($Setting.NewSetting) = $SourceGroupSettings[$Setting.UserType].($Setting.NewSetting) + "," + $string
						} else {
							$SourceGroupSettings[$Setting.UserType].($Setting.NewSetting) = $string
						}
					}
					if ($SourceGroupSettings[$Setting.UserType].($Setting.NewSetting) -eq $null -and $Setting.DefaultValue -ne $null) {
						$SourceGroupSettings[$Setting.UserType].($Setting.NewSetting) = $Setting.DefaultValue
					}
				}
					#$inifilecontents = @($inifilecontents | where {$_ -notlike "$($Setting.OldSetting)=*" })
			}

			foreach ($Setting in @('FilterAllTeachersBasedonEmailDomain','FilterAllStudentsBasedonEmailDomain','EnableExcludedTeachersGroupRecursion','EnableExcludedStudentsGroupRecursion','EnableAllTeachersSourceGroupRecursion','EnableAllStudentsSourceGroupRecursion')) {
				$inifilecontents = @($inifilecontents | where {$_ -notlike "$Setting=*" })
			}
			$inifilecontents = @($inifilecontents | where {$_ -notlike ";Example Filter the Teachers based on myschool.co.uk M365 username*" })
			$inifilecontents = @($inifilecontents | where {$_ -notlike ";Optionally Filter the found Teachers based on their M365 email address domain name*" })
			$inifilecontents = @($inifilecontents | where {$_ -notlike ";Optionally Filter the found Students based on their M365 email address domain name*" })
			$inifilecontents = @($inifilecontents | where {$_ -notlike ";In most simple situations use RequiredOrGroups, or RequiredOrRecursiveGroups*" })
			#Remove-Item "$SettingsFileName"
			if (Test-Path "$($SettingsFileName).Pre118Upgrade.INI") {
				Remove-Item "$($SettingsFileName).Pre118Upgrade.INI" | out-Null
			}
			Rename-Item "$SettingsFileName" "$($SettingsFileName).Pre118Upgrade.INI" | out-Null
			write-Host "The previous settings file $SettingsFileName has been renamed to $($SettingsFileName).Pre118Upgrade.INI" -foreground Yellow
			$SkipthisEntry = $false
			for ($a=0;$a -lt $inifilecontents.count; $a++) {
				if ($inifilecontents[$a] -eq [string]'[SourceGroups]') {
					Add-Content -Path $SettingsFileName -Value '[TeacherSourceGroups]' -Encoding ascii
					Add-Content -Path $SettingsFileName -Value ";In most simple situations use RequiredOrGroups, or RequiredOrRecursiveGroups" -Encoding ascii
					Add-Content -Path $SettingsFileName -Value ";Use RequiredOrGroups=* or RequiredOrRecursiveGroups=* to select all Users as Teachers" -Encoding ascii
					foreach ($Setting in @('RequiredGroups','RequiredRecursiveGroups','RequiredOrGroups','RequiredOrRecursiveGroups','NotRequiredGroups','NotRequiredRecursiveGroups','DisableNameorIDCheckingForGroupRules','DontAllowMissingNotRequiredGroups','IncludeDistributionGroupsInGroupRules')) {
						$string = $SourceGroupSettings["Teacher"].($Setting)
						Add-Content -Path $SettingsFileName -Value "$Setting=$string" -Encoding ascii
					}
					Add-Content -Path $SettingsFileName -Value ";Optionally Filter the found Teachers based on their M365 email address domain name - do not include the @ symbol in the filter" -Encoding ascii

					foreach ($Setting in @('DomainFilter','DontProcessStudentsFoundinTeacherSourceGroups','LogAvailableUsers')) {						
						$string = $SourceGroupSettings["Teacher"].($Setting)
						Add-Content -Path $SettingsFileName -Value "$Setting=$string" -Encoding ascii
					}
					
					Add-Content -Path $SettingsFileName -Value "" -Encoding ascii
					
					Add-Content -Path $SettingsFileName -Value '[StudentSourceGroups]' -Encoding ascii
					Add-Content -Path $SettingsFileName -Value ";In most simple situations use RequiredOrGroups, or RequiredOrRecursiveGroups" -Encoding ascii
					Add-Content -Path $SettingsFileName -Value ";Use RequiredOrGroups=* or RequiredOrRecursiveGroups=* to select all Users as Students" -Encoding ascii
					foreach ($Setting in @('RequiredGroups','RequiredRecursiveGroups','RequiredOrGroups','RequiredOrRecursiveGroups','NotRequiredGroups','NotRequiredRecursiveGroups','DisableNameorIDCheckingForGroupRules','DontAllowMissingNotRequiredGroups','IncludeDistributionGroupsInGroupRules')) {
						$string = $SourceGroupSettings["Student"].($Setting)
						Add-Content -Path $SettingsFileName -Value "$Setting=$string" -Encoding ascii
					}
					Add-Content -Path $SettingsFileName -Value ";Optionally Filter the found Students based on their M365 email address domain name - do not include the @ symbol in the filter" -Encoding ascii

					foreach ($Setting in @('DomainFilter','DontProcessTeachersFoundinStudentSourceGroups','LogAvailableUsers')) {						
						$string = $SourceGroupSettings["Student"].($Setting)
						Add-Content -Path $SettingsFileName -Value "$Setting=$string" -Encoding ascii
					}
					Add-Content -Path $SettingsFileName -Value "" -Encoding ascii
					$SkipthisEntry = $true
					
				} else {
					
					if ($inifilecontents[$a] -like "SettingsFileVersion=*") {
						$inifilecontents[$a] = "SettingsFileVersion=$SettingsFileVersion"
					}
					if ($inifilecontents[$a][0] -eq '[') {
						$SkipthisEntry = $false
					}
					if ($inifilecontents[$a] -eq [string]'[TeacherSourceGroups]' -or $inifilecontents[$a] -eq [string]'[StudentSourceGroups]') {
						$SkipthisEntry = $true
					}
					if ($SkipthisEntry -eq $false) {
						Add-Content -Path $SettingsFileName -Value $inifilecontents[$a] -Encoding ascii
					}
				}
			}
			#$inifileupgraded += 1
			write-Host "Major settings file upgrade complete" -foreground Yellow
			$inifilecontents = Get-Content $SettingsFileName
			
		}
		

		for ($a=0;$a -lt $inifilecontents.count; $a++) {

			if ($inifilecontents[$a] -like "IncludeDistributionGroups=*") {
				$inifileupgraded += 1
				$string=$null
				$string=""
				$string=$inifilecontents[$a].Split("=")[1]
				$inifilecontents[$a] = "IncludeDistributionGroupsInGroupRules=$string"
			}
			if ($inifilecontents[$a] -like "TeamsPolicyFilter=*") {
				$inifileupgraded += 1
				$string=$null
				$string=""
				$string=$inifilecontents[$a].Split("=")[1]
				$inifilecontents[$a] = "Filter=$string"
			}
			if ($inifilecontents[$a] -like "TeamsPackageFilter=*") {
				$inifileupgraded += 1
				$string=$null
				$string=""
				$string=$inifilecontents[$a].Split("=")[1]
				$inifilecontents[$a] = "Filter=$string"
			}			
			if ($inifilecontents[$a] -like "GroupFilter=*") {
				$inifileupgraded += 1
				$string=$null
				$string=""
				$string=$inifilecontents[$a].Split("=")[1]
				$inifilecontents[$a] = "Filter=$string"
			}
			if ($inifilecontents[$a] -like "GetAllUserPolicyInformationFilter=*") {
				$inifileupgraded += 1
				$string=$null
				$string=""
				$string=$inifilecontents[$a].Split("=")[1]
				$inifilecontents[$a] = "Filter=$string"
			}
			if ($inifilecontents[$a] -like "GetAllUserPolicyInformationFilterSource=*") {
				$inifileupgraded += 1
				$string=$null
				$string=""
				$string=$inifilecontents[$a].Split("=")[1]
				$inifilecontents[$a] = "FilterSource=$string"
			}				
			if ($inifilecontents[$a] -like "ListofReqdGroups=*") {
				$inifileupgraded += 1
				$string=$null
				$string=""
				$string=$inifilecontents[$a].Split("=")[1]
				$inifilecontents[$a] = "RequiredGroups=$string"
			}
			if ($inifilecontents[$a] -like "ListofReqdRecursiveGroups=*") {
				$inifileupgraded += 1
				$string=$null
				$string=""
				$string=$inifilecontents[$a].Split("=")[1]
				$inifilecontents[$a] = "RequiredRecursiveGroups=$string"
			}
			if ($inifilecontents[$a] -like "ListofReqdORGroups=*") {
				$inifileupgraded += 1
				$string=$null
				$string=""
				$string=$inifilecontents[$a].Split("=")[1]
				$inifilecontents[$a] = "RequiredOrGroups=$string"
			}
			if ($inifilecontents[$a] -like "ListofReqdORRecursiveGroups=*") {
				$inifileupgraded += 1
				$string=$null
				$string=""
				$string=$inifilecontents[$a].Split("=")[1]
				$inifilecontents[$a] = "RequiredOrRecursiveGroups=$string"
			}
			if ($inifilecontents[$a] -like "ListofNotReqdGroups=*") {
				$inifileupgraded += 1
				$string=$null
				$string=""
				$string=$inifilecontents[$a].Split("=")[1]
				$inifilecontents[$a] = "NotRequiredGroups=$string"
			}
			if ($inifilecontents[$a] -like "ListofNotReqdRecursiveGroups=*") {
				$inifileupgraded += 1
				$string=$null
				$string=""
				$string=$inifilecontents[$a].Split("=")[1]
				$inifilecontents[$a] = "NotRequiredRecursiveGroups=$string"
			}
			if ($inifilecontents[$a] -like "AddGlobalAdminToCreateTeamsUnifiedGroup=*") {
				$inifileupgraded += 1
				$string=$null
				$string=""
				$string=$inifilecontents[$a].Split("=")[1]
				$inifilecontents[$a] = "AddGlobalAdminToCreateTeamsGroup=$string"
			}
			if ($inifilecontents[$a] -like "CreateTeamsUnifiedGroupName=*") {
				$inifileupgraded += 1
				$string=$null
				$string=""
				$string=$inifilecontents[$a].Split("=")[1]
				$inifilecontents[$a] = "CreateTeamsGroupName=$string"
			}
			if ($inifilecontents[$a] -like "RemediateSkypeAccessTokenTimeout=*") {
				$inifilecontents[$a] = "CreateAzureADAccessPolicy=False"
			}
			if ($inifilecontents[$a] -like "ApplySkypeAccessTokenTimeoutRemediationWhenPoliciesAlreadyExist=*") {
				$inifileupgraded += 1
				$string=$null
				$string=""
				$string=$inifilecontents[$a].Split("=")[1]
				$inifilecontents[$a] = "ApplyAzureADAccessPolicyWhenPoliciesAlreadyExist=$string"
			}		
			if ($inifilecontents[$a] -like "ThrowWarningInsteadofErrorOnRemediationFailure=*") {
				$inifileupgraded += 1
				$string=$null
				$string=""
				$string=$inifilecontents[$a].Split("=")[1]
				$inifilecontents[$a] = "ThrowWarningInsteadofErrorOnAzureADAccessPolicyFailure=$string"			
			}	
			if ($inifilecontents[$a] -like "DontCheckOnMicrosoftCOMPrefixWhenRemovingRemediation=*") {
				$inifileupgraded += 1
				$string=$null
				$string=""
				$string=$inifilecontents[$a].Split("=")[1]
				$inifilecontents[$a] = "DontCheckOnMicrosoftCOMPrefixWhenRemovingAzureADAccessPolicy=$string"
			}
			if ($inifilecontents[$a] -like "DisableExchangeV2ConnectorCheck=*") {
				$inifileupgraded += 1
				$string=$null
				$string=""
				$string=$inifilecontents[$a].Split("=")[1]
				$inifilecontents[$a] = "DisableExchangeConnectorCheck=$string"
			}
			if ($inifilecontents[$a] -like "UseExchangeV1Connector=*") {
				$inifileupgraded += 1
			}
			
			if ($inifilecontents[$a] -like ";Always Use the OnMicrosoftCOMPrefix Domain for Skype Online, instead of Auto-detection") {
				$inifileupgraded += 1
				$inifilecontents[$a] = ";PrivateDomain is not currently used in STEPER"
			}
			if ($inifilecontents[$a] -like ";Note that the Synchronisation name is just for STEPR - it is not related to your SDS Profile Name") {
				$inifileupgraded += 1
				$inifilecontents[$a] = ";Note that the STEPERProfileName is just for STEPER - it is not related to your SDS Profile Name"
			}
			if ($inifilecontents[$a] -like "SettingsFileVersion=*") {
				$inifilecontents[$a] = "SettingsFileVersion=$SettingsFileVersion"
			}
			if ($inifilecontents[$a] -like "Protect_Password=*") {
				$FoundProtectPassword = $true
			}
			if ($inifilecontents[$a] -like "RefreshTeacherLicensingStatusAfterLicensingGroupChanges=*") {
				$FoundRefreshTeacherLicensingStatusAfterLicensingGroupChanges = $true
			}
			if ($inifilecontents[$a] -like "RefreshStudentLicensingStatusAfterLicensingGroupChanges=*") {
				$FoundRefreshStudentLicensingStatusAfterLicensingGroupChanges = $true
			}
			if ($inifilecontents[$a] -like "MaximumBatchSize=*") {
				$FoundMaxBatchSize = $true
			}
			if ($inifilecontents[$a] -like "UseLocalAttributeFiltering=*") {
				$FoundUseLocalAttributeFiltering = $true
			}
			if ($inifilecontents[$a] -like "GetExtraStatistics=*") {
				$FoundGetExtraStatistics = $true
			}
			if ($inifilecontents[$a] -like "GlobalOnFailureApplytoAllUsersOnNextRun=*") {
				$FoundGlobalOnFailureApplytoAllUsersOnNextRun = $true
			}
			if ($inifilecontents[$a] -like "UseAzureADforLocalAttributeFilteringByDefault=*") {
				$FoundUseAzureADforLocalAttributeFilteringByDefault = $true
			}
			if ($inifilecontents[$a] -like "DisableAutoUpdate=*") {
				$FoundDisableAutoUpdate = $true
			}
			if ($inifilecontents[$a] -like "UseCSVforUserDataInLogFile=*") {
				$FoundUseCSVforUserDataInLogFile = $true
			}
			if ($inifilecontents[$a] -like "RepeatLogEventThreshold=*") {
				$FoundRepeatLogEventThreshold = $true
			}
			if ($inifilecontents[$a] -like "GetUserPackageInformation=*") {
				$FoundGetPackageInformation = $true
				if (($a+1)-lt $inifilecontents.count) {
					if ($inifilecontents[$a+1] -like "GetAllUserPolicyInformation=*") { # 1.0.92 - swap these around
						$inifileupgraded += 1
						$string = $inifilecontents[$a]
						$inifilecontents[$a] = $inifilecontents[$a+1]
						$inifilecontents[$a+1] = $string
					} else {
						if (($a+2)-lt $inifilecontents.count) {
							if ($inifilecontents[$a+2] -like "GetAllUserPolicyInformation=*") { # 1.0.92 - swap these around
								$inifileupgraded += 1
								$string = $inifilecontents[$a]
								$inifilecontents[$a] = $inifilecontents[$a+2]
								$inifilecontents[$a+2] = $string
							}
						}
					}
				}
			}
			if ($inifilecontents[$a] -like '`[Remediation`]') {
				$FoundRemediationSection = $true
			}
			if ($inifilecontents[$a] -like '`[Override`]') {
				$FoundOverrideSection = $true
			}
			if ($inifilecontents[$a] -like '`[FunctionControl`]') {
				$FoundFunctionControl = $true
			}
			if ($inifilecontents[$a] -like '`[Collections`]') {
				$FoundCollections = $true
			}
			if ($inifilecontents[$a] -like "UseSkypeforBusinessPowerShell=*" -or $inifilecontents[$a] -like "ForceLyncAdminDNSDomain=*" -or $inifilecontents[$a] -like "DisableSkypeOnlineConnectorCheck=*" -or $inifilecontents[$a] -like ";Note that the Skype Online Connector must be installed manually from*") {
				$FoundLinesToRemove = $true
			}
		}
		if ($FoundOverrideSection -eq $false) {
			$FoundGlobalOnFailureApplytoAllUsersOnNextRun = $true	#  if we havent found the section, then this line will be added when the new section is installed
		}

		if ($inifileupgraded -gt 0 -or $FoundDisableAutoUpdate -eq $false -or $FoundRepeatLogEventThreshold -eq $false -or $FoundRemediationSection -eq $false -or $FoundFunctionControl -eq $false -or $FoundOverrideSection -eq $false -or $FoundUseCSVforUserDataInLogFile -eq $false -or $FoundMaxBatchSize -eq $false -or $FoundUseLocalAttributeFiltering -eq $false -or $FoundUseAzureADforLocalAttributeFilteringByDefault -eq $false -or $FoundGetExtraStatistics -eq $false -or $FoundProtectPassword -eq $false -or $FoundGlobalOnFailureApplytoAllUsersOnNextRun -eq $false -or $FoundGetPackageInformation -eq $false -or $FoundCollections -eq $false -or $FoundRefreshTeacherLicensingStatusAfterLicensingGroupChanges -eq $false -or $FoundRefreshStudentLicensingStatusAfterLicensingGroupChanges -eq $false -or $FoundLinesToRemove -eq $true) {
			#$inifilecontents | Set-Content $SettingsFileName
			write-Host "Upgrading the settings file" -foreground Yellow
			Remove-Item "$SettingsFileName"
			for ($a=0;$a -lt $inifilecontents.count; $a++) {
				$SkipthisEntry = $false

				if ($FoundCollections -eq $false) {
					if ($inifilecontents[$a] -like [string]'`[Misc`]') {
						Add-Content -Path "$SettingsFileName" -Value '[Collections]' -Encoding ascii
                        Add-Content -Path "$SettingsFileName" -Value 'IgnoreCollectionIDUserType=False' -Encoding ascii
						Add-Content -Path "$SettingsFileName" -Value "" -Encoding ascii
						$inifileupgraded += 1
					}
				}
				
				if ($FoundRemediationSection -eq $false) {
					if ($inifilecontents[$a] -like [string]'`[Misc`]') {
						Add-Content -Path "$SettingsFileName" -Value '[Remediation]' -Encoding ascii
                        Add-Content -Path "$SettingsFileName" -Value 'OnlyRemediateNewTeachers=True' -Encoding ascii
                        Add-Content -Path "$SettingsFileName" -Value 'OnlyRemediateNewStudents=True' -Encoding ascii
                        Add-Content -Path "$SettingsFileName" -Value 'OnFailureApplytoAllUsersOnNextRun=False' -Encoding ascii
						Add-Content -Path "$SettingsFileName" -Value "" -Encoding ascii
						$inifileupgraded += 3
					}
				}
			
				if ($FoundFunctionControl -eq $false) {
					if ($inifilecontents[$a] -like [string]'`[Misc`]') {
						Add-Content -Path "$SettingsFileName" -Value '[FunctionControl]' -Encoding ascii
						Add-Content -Path "$SettingsFileName" -Value 'TurnOffTeacherFilteredSecurityGroupFunctionality=False' -Encoding ascii
						Add-Content -Path "$SettingsFileName" -Value 'TurnOffStudentFilteredSecurityGroupFunctionality=False' -Encoding ascii
						Add-Content -Path "$SettingsFileName" -Value 'TurnOffTeacherFilteredUnifiedGroupFunctionality=False' -Encoding ascii
						Add-Content -Path "$SettingsFileName" -Value 'TurnOffStudentFilteredUnifiedGroupFunctionality=False' -Encoding ascii
						Add-Content -Path "$SettingsFileName" -Value 'TurnOffTeacherFilteredDistributionGroupFunctionality=False' -Encoding ascii
						Add-Content -Path "$SettingsFileName" -Value 'TurnOffStudentFilteredDistributionGroupFunctionality=False' -Encoding ascii
						Add-Content -Path "$SettingsFileName" -Value 'TurnOffTeacherFilteredPackageFunctionality=False' -Encoding ascii
						Add-Content -Path "$SettingsFileName" -Value 'TurnOffStudentFilteredPackageFunctionality=False' -Encoding ascii						
						Add-Content -Path "$SettingsFileName" -Value 'TurnOffTeacherFilteredPolicyFunctionality=False' -Encoding ascii
						Add-Content -Path "$SettingsFileName" -Value 'TurnOffStudentFilteredPolicyFunctionality=False' -Encoding ascii						
						Add-Content -Path "$SettingsFileName" -Value "" -Encoding ascii
						$inifileupgraded += 10
					}
				}
				
				if ($FoundOverrideSection -eq $false) {
					if ($inifilecontents[$a] -like [string]'`[LicensingGroups`]') {
						Add-Content -Path "$SettingsFileName" -Value '[Override]' -Encoding ascii
						Add-Content -Path "$SettingsFileName" -Value 'GlobalOnFailureApplytoAllUsersOnNextRun=True' -Encoding ascii
						Add-Content -Path "$SettingsFileName" -Value ';The settings below only take effect if set to True' -Encoding ascii
                        Add-Content -Path "$SettingsFileName" -Value 'ApplytoAllTeachers=False' -Encoding ascii
                        Add-Content -Path "$SettingsFileName" -Value 'ApplytoAllStudents=False' -Encoding ascii
                        Add-Content -Path "$SettingsFileName" -Value "" -Encoding ascii
						$inifileupgraded += 3
					}
				}
				
				if ($inifilecontents[$a] -like "UseExchangeV1Connector=*") {
					$SkipthisEntry = $true
				}
				
				
				if ($inifilecontents[$a] -like "UseSkypeforBusinessPowerShell=*" -or $inifilecontents[$a] -like "ForceLyncAdminDNSDomain=*" -or $inifilecontents[$a] -like "DisableSkypeOnlineConnectorCheck=*" -or $inifilecontents[$a] -like ";Note that the Skype Online Connector must be installed manually from*") {
					$SkipthisEntry = $true
					$inifileupgraded += 1
				}
				
				if ($SkipthisEntry -eq $false) {
					Add-Content -Path "$SettingsFileName" -Value $inifilecontents[$a] -Encoding ascii
				}
				if ($FoundMaxBatchSize -eq $false) {
					if ($inifilecontents[$a] -like "StudentTeamsPolicyBatchProcessingThreshold=*") {
						Add-Content -Path "$SettingsFileName" -Value "MaximumBatchSize=5000" -Encoding ascii
						$inifileupgraded += 1
					}
				}
		
				if ($FoundRefreshTeacherLicensingStatusAfterLicensingGroupChanges -eq $false) {
					if ($inifilecontents[$a] -like "SleepSecondsAfterApplyingLicensing=*") {
						Add-Content -Path "$SettingsFileName" -Value "RefreshTeacherLicensingStatusAfterLicensingGroupChanges=False" -Encoding ascii
						$inifileupgraded += 1
					}
				}
								
				if ($FoundRefreshStudentLicensingStatusAfterLicensingGroupChanges -eq $false) {
					if ($inifilecontents[$a] -like "SleepSecondsAfterApplyingLicensing=*") {
						Add-Content -Path "$SettingsFileName" -Value "RefreshStudentLicensingStatusAfterLicensingGroupChanges=False" -Encoding ascii
						$inifileupgraded += 1
					}
				}
														
				if ($FoundGlobalOnFailureApplytoAllUsersOnNextRun -eq $false) {
					if ($inifilecontents[$a] -like [string]'`[Override`]') {
						Add-Content -Path "$SettingsFileName" -Value "GlobalOnFailureApplytoAllUsersOnNextRun=True" -Encoding ascii
						$inifileupgraded += 1
					}
				}
				if ($FoundProtectPassword -eq $false) {
					if ($inifilecontents[$a] -like [string]'`[Credentials`]') {
						Add-Content -Path "$SettingsFileName" -Value "Protect_Password=True" -Encoding ascii
						$inifileupgraded += 1
					}
				}
				if ($FoundDisableAutoUpdate -eq $false) {
					if ($inifilecontents[$a] -like [string]'`[Misc`]') {
						Add-Content -Path "$SettingsFileName" -Value "DisableAutoUpdate=False" -Encoding ascii
						$inifileupgraded += 1
					}
				}
				if ($FoundUseLocalAttributeFiltering -eq $false) {
					if ($inifilecontents[$a] -like [string]'`[Misc`]') {
						Add-Content -Path "$SettingsFileName" -Value "UseLocalAttributeFiltering=False" -Encoding ascii
						$inifileupgraded += 1
					}
				}

				if ($FoundGetExtraStatistics -eq $false) {
					if ($inifilecontents[$a] -like [string]'`[Misc`]') {
						Add-Content -Path "$SettingsFileName" -Value "GetExtraStatistics=True" -Encoding ascii
						$inifileupgraded += 1
					}
				}

				if ($FoundUseAzureADforLocalAttributeFilteringByDefault -eq $false) {
					if ($inifilecontents[$a] -like [string]'`[Misc`]') {
						Add-Content -Path "$SettingsFileName" -Value "UseAzureADforLocalAttributeFilteringByDefault=True" -Encoding ascii
						$inifileupgraded += 1
					}
				}
				if ($FoundRepeatLogEventThreshold -eq $false) {
					if ($inifilecontents[$a] -like [string]'`[Misc`]') {
						Add-Content -Path "$SettingsFileName" -Value "RepeatLogEventThreshold=20" -Encoding ascii
						$inifileupgraded += 1
					}
				}
				if ($FoundUseCSVforUserDataInLogFile -eq $false) {
					if ($inifilecontents[$a] -like [string]'`[Misc`]') {
						Add-Content -Path "$SettingsFileName" -Value "UseCSVforUserDataInLogFile=False" -Encoding ascii
						$inifileupgraded += 1
					}
				}
				if ($FoundGetPackageInformation -eq $false) {
					#if ($inifilecontents[$a] -like [string]'`[InformationRetrieval`]') {
					if ($inifilecontents[$a] -like "GetAllUserPolicyInformation=*") { # 1.0.92
						Add-Content -Path "$SettingsFileName" -Value 'GetUserPackageInformation=False' -Encoding ascii
					$inifileupgraded += 1	
					}
				}				
			}
		}
		
		if ($inifileupgraded -gt 0) {
			$inifilecontents = Get-Content $SettingsFileName # 1.0.53
			$Actions += "- The settings file $SettingsFileName (previously last modified $inifilewritetime) has been upgraded with $inifileupgraded changes"
			write-Host "The settings file $SettingsFileName (previously last modified $inifilewritetime) has been upgraded with $inifileupgraded changes" -foreground Yellow
			write-host
			$heldinifilewritetime = $inifilewritetime
			$inifilewritetime = (Get-ChildItem $SettingsFileName).LastWriteTime
			$inifilewritetime = $inifilewritetime.ToString("dd-MM-yyyy HH:mm:ss")
		}
	} else {
	$MajorErrors += "- Unable to read the Settings from $SettingsFileName"
    write-Host "Unable to read the Settings from $SettingsFileName" -foreground Red
    write-host
	}
}


if ($MajorErrors.Count -eq 0) {
	$iniFile = $null
	$iniFile = @()
	$iniFile, $INIFileOK = Get-IniFile $SettingsFileName
	#if (Test-Path $SettingsFileName) { write-host "Found $SettingsFileName" } else {  write-host "Not Found $SettingsFileName" }

    if (! $IniFile) {
	    $MajorErrors += "- Unable to read the Settings from $SettingsFileName"
        write-Host "Unable to read the Settings from $SettingsFileName" -foreground Red
        write-host
    } else {
	    $Actions += "- Loaded Settings from $SettingsFileName (last modified $inifilewritetime)"
        write-Host "Loading Settings from $SettingsFileName" -foreground Yellow
        write-host
    }
}


$hash = "(not calculated)"

if ($PSBoundParameters.count -gt 0) {
    $PSBoundParametersCR = ( $PSBoundParameters | ft * -AutoSize | Out-String -Width 4096 )
} else {
    $PSBoundParametersCR = "(No Bound / Recognised Command Line Arguments)" | Out-String
}

if ($MyInvocation.UnBoundArguments) {
    $UnBoundParametersCR = $MyInvocation.UnBoundArguments | Out-String
} else {
    $UnBoundParametersCR = "(No Unbound / Unrecognised Command Line Arguments)" | Out-String
}

if ($MajorErrors.Count -eq 0) {
	
    # calculate program hash
    $myname = $MyInvocation.MyCommand.Definition
	$MyFolder = split-path -path $myname -parent
	if ($MyFolder[$MyFolder.Length-1] -ne '\') {
		$MyFolder="$MyFolder\"
	}
    if ($myname) {
        $thisfile = get-content $myname

        if ($thisfile) {
            $hash = Get-StringHash $thisfile
        }
    }


	# Synchronisation Profile Name
	$string = $null
	$string = $iniFile.General.STEPERProfileName

	$stringb = $null
	$stringb = $iniFile.Tenancy.OnMicrosoftCOMPrefix
    if ($stringb) {
		#if ($stringb -ne "myschool") {
			$stringb = "-$stringb"
		#}
    }

	$ProfileName = "(Untitled$stringb)"

	if ($string) {
		$ProfileName = $String.replace('\','').Replace('*','').Replace('.','').Replace('"','').Replace('/','').Replace('[','').Replace(']','').Replace(';','').Replace('|','').Replace(',','').Replace('?','').Replace('<','').Replace('>','')
    }
    if ($ProfileName.Length -eq 0) {
        $ProfileName = "(Untitled$stringb)"
    } else {
		$AppLogName = "$AppLogName $ProfileName"
    }
    $Host.UI.RawUI.WindowTitle = $myInvocation.MyCommand.Definition + " " + $ProgramName + " " + $Version + " (C:" + $Channel + ":" + $PatchLevel + ") Profile: " + $ProfileName + " (Elevated)"
	#$Host.UI.RawUI.WindowTitle = $myInvocation.MyCommand.Definition + " " + $ProgramName + " " + $Version + " Profile: " + $ProfileName + " (Elevated)"
    $regpath = "HKLM:\SOFTWARE\Stone\M365\$ProgramName\Profiles\$ProfileName"

    # Get Verbosity
    if ($iniFile.Misc.Verbose -eq $true -or $iniFile.Misc.Verbose -eq 1) {
		$Verbose = $true
    }
    if ($Verbose -eq $true) {
        $OtherSettings += "- Verbose: True"
	} else {
        $OtherSettings += "- Verbose: False"
    }

    # Setup Event Log
	if ([System.Diagnostics.EventLog]::SourceExists("$AppLogName") -eq $false) { #1.0.94
		New-EventLog -LogName Application -Source $AppLogName -ErrorAction SilentlyContinue
	}

    if ($Verbose -eq $true) {
        Write-EventLog –LogName Application –Source $AppLogName –EntryType Information –EventID $AppLogBaseIndex -Category $AppLogCategory -Message ("$Caption - $ProgramName Version: $Version - Started`r`nChannel / PatchLevel: $Channel / $PatchLevel`r`nPID: $Pid`r`n`r`nProgram Location: $Myname`r`nSettings File: $SettingsFileName`r`nProfile Name: $ProfileName`r`n`r`nBound / Recognised Command Line Arguments:`r`n$PSBoundParametersCR`r`nUnbound / Unrecognised Command Line Arguments:`r`n$UnBoundParametersCR")
    }
	if ($inifileupgraded -gt 0) {
		Write-EventLog –LogName Application –Source $AppLogName –EntryType Information –EventID ($AppLogBaseIndex+10) -Category $AppLogCategory -Message ("$Caption - $ProgramName Version: $Version`r`nChannel / PatchLevel: $Channel / $PatchLevel`r`nPID: $Pid`r`n`r`nThe settings file $SettingsFileName (previously last modified $heldinifilewritetime) has been upgraded with $inifileupgraded changes")	
	}


    # Ensure Execution Policy
    set-executionpolicy unrestricted -Scope Process -Force -Confirm:$false

	# Ensure formatted lists arent cut off
	$global:FormatEnumerationLimit = -1

    # Setup Logging
    if ($iniFile.Misc.DisableDetailedLogFile -eq $true -or $iniFile.Misc.DisableDetailedLogFile -eq 1) {
        $DetailedLoggingEnabled = $false
		write-host "Log file logging has been disabled" -ForegroundColor Yellow
    } else {
        $DetailedLoggingEnabled = $true
    }
    if ($DetailedLoggingEnabled -eq $true -or $iniFile.InformationRetrieval.GetAllUserPolicyInformation -eq $true -or $iniFile.InformationRetrieval.GetAllUserPolicyInformation -eq 1) {

        if ($iniFile.Misc.DetailedLogFileFolder) {
            $DetailedLogFileFolder = $iniFile.Misc.DetailedLogFileFolder
            New-Item -ItemType Directory -Path $DetailedLogFileFolder | Out-Null
            if (! (Test-Path $DetailedLogFileFolder)) {
                $Warnings += "- Unable to use the specified Log File Folder $DetailedLogFileFolder"
                write-host "Unable to use the specified Log File Folder $DetailedLogFileFolder" -ForegroundColor Yellow
                $DetailedLogFileFolder = split-path -parent $MyInvocation.MyCommand.Definition
                if ($DetailedLogFileFolder[$DetailedLogFileFolder.Length-1] -eq '\') {
                    $DetailedLogFileFolder = "$($DetailedLogFileFolder)Logs"
                } else {
                    $DetailedLogFileFolder = "$($DetailedLogFileFolder)\Logs"
                }
            }
        } else {
            #$DetailedLogFileFolder = split-path -parent $MyInvocation.MyCommand.Definition
            $DetailedLogFileFolder = split-path -parent $MyInvocation.MyCommand.Definition
            if ($DetailedLogFileFolder[$DetailedLogFileFolder.Length-1] -eq '\') {
                $DetailedLogFileFolder = "$($DetailedLogFileFolder)STEPER Logs"
            } else {
                $DetailedLogFileFolder = "$($DetailedLogFileFolder)\STEPER Logs"
            }
        }

        if (!(Test-Path $DetailedLogFileFolder) ) {
            mkdir "$DetailedLogFileFolder" | out-null
        }
        if (! (Test-Path $DetailedLogFileFolder)) {
            $Warnings += "- Unable to use the specified Log File Folder $DetailedLogFileFolder, logging is now disabled"
            write-host "Unable to use the specified Log File Folder $DetailedLogFileFolder, logging is now disabled" -ForegroundColor Yellow
            $DetailedLoggingEnabled = $false
        }

        $string = $SettingsFileName | Split-Path -leaf

        if ($DetailedLogFileFolder[$DetailedLogFileFolder.Length-1] -eq '\') {
            $LogFile = "$DetailedLogFileFolder$ProgramName $Version $HostName $ProfileName $datenow Log File.txt"
            $UserFile = "$DetailedLogFileFolder$ProgramName $Version $HostName $ProfileName $datenow User Export.csv"
            $ZipFile = "$DetailedLogFileFolder$ProgramName $Version $HostName $ProfileName $datenow Log Bundle.zip"
            $CopySettings = "$DetailedLogFileFolder$ProgramName $Version $HostName $ProfileName $datenow $PID $string"
        } else {
            $LogFile = "$DetailedLogFileFolder\$ProgramName $Version $HostName $ProfileName $datenow Log File.txt"
            $UserFile = "$DetailedLogFileFolder\$ProgramName $Version $HostName $ProfileName $datenow User Export.csv"
            $ZipFile = "$DetailedLogFileFolder\$ProgramName $Version $HostName $ProfileName $datenow Log Bundle.zip"
            $CopySettings = "$DetailedLogFileFolder\$ProgramName $Version $HostName $ProfileName $datenow $PID $string"
        }
        


        if ($DetailedLoggingEnabled -eq $true) {
            if (Test-Path $Logfile) { Remove-Item $Logfile }
        
            Add-Content -Path "$LogFile" -Value "$Caption - $ProgramName Version: $Version Channel / PatchLevel: $Channel / $PatchLevel - $Author $Date" -Encoding Unicode
			Add-Content -Path "$LogFile" -Value "ProcessID: $PID" -Encoding Unicode
			
			Add-Content -Path "$LogFile" -Value "`r`nBound / Recognised Command Line Arguments" -Encoding Unicode
			Add-Content -Path "$LogFile" -Value "=========================================" -Encoding Unicode
			if ($PSBoundParameters.count -gt 0) {
				$PSBoundParameters | ft * -AutoSize | Out-String -Width 4096 | Out-File -filepath "$LogFile" -append -Encoding Unicode
			} else {
				Add-Content -Path "$LogFile" -Value "(No Bound / Recognised Command Line Arguments)`r`n" -Encoding Unicode	
			}

			Add-Content -Path "$LogFile" -Value "`r`nUnbound / Unrecognised Command Line Arguments" -Encoding Unicode
			Add-Content -Path "$LogFile" -Value "=============================================" -Encoding Unicode
			if ($MyInvocation.UnBoundArguments) {
				$MyInvocation.UnBoundArguments | ft * -AutoSize | Out-String -Width 4096 | Out-File -filepath "$LogFile" -append -Encoding Unicode
			} else {
				Add-Content -Path "$LogFile" -Value "(No Unbound / Unrecognised Command Line Arguments)`r`n" -Encoding Unicode	
			}
			
            if (!(Test-Path $Logfile)) {
                $DetailedLoggingEnabled = $false
                write-host "Unable to Create Detailed Log File $Logfile" -ForegroundColor Yellow
                $Warnings += "- Unable to Create Detailed Log File $Logfile"
                Write-EventLog –LogName Application –Source $AppLogName –EntryType Error –EventID ($AppLogBaseIndex+6) -Category $AppLogCategory -Message ("$Caption - $ProgramName Version: $Version - Started`r`nChannel / Patch Level: $Channel / $PatchLevel`r`n`r`nProgram Location: $Myname`r`nSettings File: $SettingsFileName`r`nProfile Name: $ProfileName`r`n`r`nWarning - Unable to Create Detailed Log File $Logfile")
            } else {
            $Actions += "- Detailed Log Information being saved in $LogFile"
            write-Host "Detailed Log Information being saved in $LogFile" -foreground Yellow
            }
        }

        if (Test-Path $ZipFile) { Remove-Item $ZipFile }

        if ($iniFile.InformationRetrieval.GetAllUserPolicyInformation -eq $true -or $iniFile.InformationRetrieval.GetAllUserPolicyInformation -eq 1) {
            if (Test-Path $UserFile) { Remove-Item $UserFile }
            Add-Content -Path "$UserFile" -Value "$Caption - $ProgramName Version: $Version $Author $Date" -Encoding Unicode
            if (!(Test-Path $UserFile)) {
                $Warnings += "- Unable to create the GetAllUserPolicyInformation Output file $UserFile"
                write-Host "Unable to create the GetAllUserPolicyInformation Output file $UserFile" -foreground Yellow
                if ($DetailedLoggingEnabled -eq $true) {
                    Add-Content -Path "$LogFile" -Value "Unable to create the GetAllUserPolicyInformation Output file $UserFile" -Encoding Unicode
                }
                $UserFile = $null
                #$Actions += "- Detailed Log Information being saved in $UserFile"
                #write-Host "Detailed Log Information being saved in $UserFile" -foreground Yellow
            }
        } else {
            $UserFile = $null
        }
    }

    #exit 0

    $now = Get-Date
    $NewMonth = $false

    # Profile Specific
    if ((Get-RegistryValue -Path "$regpath\Program Information" -Name 'CurrentMonth' -ValueType DWORD ) -ne $null) {
        if ((Get-RegistryValue -Path "$regpath\Program Information" -Name 'CurrentMonth' -ValueType DWORD ) -eq $now.month) {
            if ((Get-RegistryValue -Path "$regpath\Program Information" -Name 'RunCountThisMonth' -ValueType DWORD ) -ne $null) {
    			Set-RegistryValue -Path "$regpath\Program Information" -Name 'RunCountThisMonth' -ValueType DWORD -Value ((Get-RegistryValue -Path "$regpath\Program Information" -Name 'RunCountThisMonth' -ValueType DWORD )+1)
		    } else {
		    Set-RegistryValue -Path "$regpath\Program Information" -Name 'RunCountThisMonth' -ValueType DWORD -Value 1
            }
        } else {
        #$NewMonth = $true
        [int]$RunCountPreviousMonth = Get-RegistryValue -Path "$regpath\Program Information" -Name 'RunCountThisMonth' -ValueType DWORD
	    Set-RegistryValue -Path "$regpath\Program Information" -Name 'RunCountPreviousMonth' -ValueType DWORD -Value $RunCountPreviousMonth
	    Set-RegistryValue -Path "$regpath\Program Information" -Name 'RunCountThisMonth' -ValueType DWORD -Value 1
        Set-RegistryValue -Path "$regpath\Program Information" -Name 'CurrentMonth' -ValueType DWORD -Value $now.month
	    }
    } else {
        Set-RegistryValue -Path "$regpath\Program Information" -Name 'RunCountThisMonth' -ValueType DWORD -Value 1
        Set-RegistryValue -Path "$regpath\Program Information" -Name 'CurrentMonth' -ValueType DWORD -Value $now.month
    }

    # Global
    if ((Get-RegistryValue -Path "$regalt\Global Information" -Name 'CurrentMonth' -ValueType DWORD ) -ne $null) {
        if ((Get-RegistryValue -Path "$regalt\Global Information" -Name 'CurrentMonth' -ValueType DWORD ) -ne $now.month) {
            $NewMonth = $true
            #Set-RegistryValue -Path "$regalt\Global Information" -Name 'CurrentMonth' -ValueType DWORD -Value $now.month

            # this is set later after all of the modules have been updated
	    }
    } else {
        Set-RegistryValue -Path "$regalt\Global Information" -Name 'CurrentMonth' -ValueType DWORD -Value $now.month
    }
} else {
    New-EventLog -LogName Application -Source $AppLogName -ErrorAction SilentlyContinue	
}

if ($MajorErrors.Count -eq 0 -and ($inifile.Misc.CheckforUpdates -eq $false -or $inifile.Misc.CheckforUpdates -eq 0)) {
    write-host
    $PreviouslyKilled = (Get-RegistryValue -Path "$regalt\Versions\$version" -Name 'Killed' -ValueType DWORD )
    if ($PreviouslyKilled -eq 1) {
        $UpdatesAvailable += "- This version of STEPER has been disabled - please update this program"
        write-host "ERROR: This version of STEPER has been disabled - please update this program" -Foregroundcolor red
        write-host
		Write-EventLog –LogName Application –Source $AppLogName –EntryType Error –EventID ($AppLogBaseIndex+20) -Category $AppLogCategory -Message ("$Caption - $ProgramName Version: $Version`r`nChannel / PatchLevel: $Channel / $PatchLevel`r`nPID: $Pid`r`n`r`nERROR: This version of STEPER has been disabled - please update this program")
        $MajorErrors += $UpdatesAvailable
    }
}

if ($INIFileOK -eq $false) {
	$MajorErrors += "- The settings file $SettingsFileName contains duplicated sections"
	write-host
	write-Host "The settings file $SettingsFileName contains duplicated sections" -foreground red

}

$tlsversion = ([Net.ServicePointManager]::SecurityProtocol)

if ($MajorErrors.Count -eq 0 -and !($iniFile.PowerShell.AutoSetTLSSecurity -eq $false -or $iniFile.PowerShell.AutoSetTLSSecurity -eq 0)) { # 1.0.52
	if ($PSVersionTable.PSVersion.Build -le 17134) {
	    write-host
		write-host "Setting Security Protocol to TLS1.2" -foregroundcolor green	
        $Actions += "- Setting Security Protocol to TLS1.2"
        if ($DetailedLoggingEnabled) {
            Add-Content -Path "$LogFile" -Value "Setting Security Protocol to TLS1.2" -Encoding Unicode
        }
        [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
	}
}

# Known Windows Versions that require fixing
# 1803 PSVersion.Build = 17134  Revision = 112

# Windows Versions that do not require fixing
# Server 1809 with Updates PSVersion.Build = 17763  Revision = 1007


$Motd = "(no message)"
[int]$Motdtype = 0

# motd types
# 0 none
# 1 information
# 2 warning
# 3 error

$OLHash = $null
$PreRunPause=$false

if ($MajorErrors.Count -eq 0) {
    write-host
# Check for Updates
	if ($inifile.Misc.CheckforUpdates -ne $false -and $inifile.Misc.CheckforUpdates -ne 0) {
		$obj = @()
        $obj = Invoke-WebRequest "https://kbftp.stonegroup.co.uk/steper/steper.csv" -TimeoutSec 30 -UseBasicParsing

		if ($obj) {
			$history = convertfrom-csv $obj

            $output = @()
			$output = @($history | where {[int]$_.Version_Major -eq [int]$VersionMajor -and [int]$_.Version_Minor -eq [int]$VersionMinor -and [int]$_.Version_Trivial -eq [int]$VersionTrivial -and [int]$_.Channel -eq [int]$Channel}) | Select -Last 1
            if ($output) {
                if ($output.kill -eq 1 -or $output.kill -eq 0) {
                    Set-RegistryValue -Path "$regalt\Versions\$version" -Name 'Killed' -ValueType DWORD -Value $output.kill
                }

				if (($output.UpdateModulesNow -eq 1) -and ($inifile.PowerShell.DisableRemoteModuleUpdateRequests -ne $True -and $inifile.PowerShell.DisableRemoteModuleUpdateRequests -ne 1)) {
					$monthlyupdatecheck=$true
				}

                if ($output.MotdType -eq 0 -or $output.MotdType -eq 1 -or $output.MotdType -eq 2 -or $output.MotdType -eq 3) {
                    Set-RegistryValue -Path "$regalt\Versions\$version" -Name 'MotdType' -ValueType DWORD -Value $output.MotdType
					Remove-ItemProperty -Path "$regalt\Versions\$version" -Name 'Motd' -Force -Erroraction SilentlyContinue
					if ($output.MotdType -eq 1 -or $output.MotdType -eq 2 -or $output.MotdType -eq 3) {
						Set-RegistryValue -Path "$regalt\Versions\$version" -Name 'Motd' -ValueType String -Value $output.Motd
                    }
				}
            
				if ($Output.Hash) {
					Set-RegistryValue -Path "$regalt\Versions\$version" -Name 'OLHash' -ValueType String -Value $output.Hash
					$OLHash = $output.Hash
				}
				
				if (($Output.AutoUpdate -eq 1) -and ($inifile.Misc.DisableAutoUpdate -ne $True -and $inifile.Misc.DisableAutoUpdate -ne 1)) {
					if (!([int]$Output.PatchLevel) -or [int]$PatchLevel -eq 0 -or [int]$Output.PatchLevel -gt $Patchlevel) {
						$RunFile=$null
						$TempFile=$null
						$NoUpdate=$false
						if ($OLHash -eq $hash) {
							if ($Output.DownloadLink) {
								$FileName = Split-path -leaf $Output.DownloadLink

								$warning=$false
								
								
								#if ((($FileName.Split(".")[-1].ToUpper()) -ne "ZIP") -or $FileName.Runfile.Split(".").count -lt 2) {  # cant be ZIP file
                                if ((($FileName.Split(".")[-1].ToUpper()) -ne "ZIP") -or $Output.Runfile.Split(".").count -lt 2) {  # cant be ZIP file # 1.0.76
									if ($Output.OverwriteExisting -eq 2) {
										if ($Output.ReplaceExistingRunFile -eq 1 -and !( Test-Path $MyName)) { # should not happen
											$warning=$true
										}
										if ($Output.ReplaceExistingRunFile -ne 1 -and !( Test-Path "$MyFolder$FileName")) {
											$warning=$true
										}
									}
									if ($Output.OverwriteExisting -eq 1) {
										#if ($Output.ReplaceExistingRunFile -eq 1 -and ( Test-Path $MyName)) { # will always happen if OverwriteExisting = 1 and ReplaceExistingRunFile = 1
										#	$warning=$true
										#}
										if ($Output.ReplaceExistingRunFile -ne 1 -and ( Test-Path "$MyFolder$FileName")) {
											$warning=$true
										}
									}
								}
								
								if ($warning -eq $false) {
								
									#if ($Output.ReplaceExistingRunFile -eq 1 -and ($Output.Runfile)) {
									#	if ((($Output.Runfile.Split(".")[-1].ToUpper()) -ne "PS1") -or  $Output.Runfile.Split(".").count -lt 2 ) { # cant be PS1 file
									#		$warning=$true
									#	}
									#}

								
									if ($warning -eq $false) {

										if ($FileName.Split(".").count -gt 1) {
											[String]$TempFile = $MyFolder + ([system.io.fileinfo]"$FileName").basename + "." + [string]$PID + "." + $HostName + "." + $FileName.Split(".")[-1]
										} else {
											[String]$TempFile = $MyFolder + ([system.io.fileinfo]"$FileName").basename + "." + [string]$PID + "." + $HostName
										}
							
									
										if (!($TempFile) -or (!($PID))) {
											[string]$TempFile = $MyFolder + $HostName + (split-path -leaf $Output.DownloadLink)								
										}
										#} else {
										#    if ($Output.ReplaceExistingRunFile -eq 1 -and ($Output.Runfile)) {
										#        $TempFile=$MyName
										#        [string]$RunFile = $MyName
										#    } else {
										#        [string]$TempFile = $MyFolder + (split-path -leaf $Output.DownloadLink)	
										#        if ($Output.Runfile) {
										#            [string]$RunFile = $TempFile
										#        }
										#    }

										#}
										if ( Test-Path $TempFile) {
											Remove-Item $TempFile -Force | out-Null
										}
										if ( !(Test-Path $TempFile)) {
											Invoke-WebRequest ($Output.DownloadLink) -TimeoutSec 30 -OutFile $TempFile -UseBasicParsing
											if (Test-Path $TempFile) {
												$thisfile = $null
												$thisfile = get-content $TempFile
												$Updatehash = Get-StringHash $thisfile
												$thisfile = $null
												$warning=$false
												if ( !($Output.UpdateFileHash) -or $Output.UpdateFileHash -eq $Updatehash -or ($inifile.Misc.IgnoreAutoUpdateHash -eq $True -or $inifile.Misc.IgnoreAutoUpdateHash -eq 1)) {
													if ((($Tempfile.Split(".")[-1].ToUpper()) -eq "ZIP") -and $Tempfile.Split(".").count -gt 1) {
														Add-Type -AssemblyName System.IO.Compression.FileSystem
													
														#$ExtractShell = New-Object -ComObject Shell.Application 
														#$Files = $ExtractShell.Namespace($TempFile).Items()
														#$ExtractShell.NameSpace($MyFolder).CopyHere($Files, 0x14)
														$zipFileDownload = $null
														$zipFileItem = $null
														$OutPath = $null
														
														$zipFileItem = Get-Item $TempFile
														
														if ($zipFileItem) {	
															$zipFileDownload = [System.IO.Compression.ZipFile]::OpenRead($zipFileItem.FullName)
															foreach($entry in $zipFileDownload.Entries) {
																if ($Output.ReplaceExistingRunFile -eq 1 -and ($Output.Runfile) -and $entry.FullName.Replace("/","\") -like $Output.Runfile) {
																	[string]$RunFile = $MyName
																	#[string]$targetFileName = $MyName
																	[string]$targetFileName =""
																} else {
																	[string]$targetFileName = $MyFolder + $entry.FullName.Replace("/","\")
																}
																if ($targetFileName) {
																	if (($Output.OverwriteExisting -eq 2 -and !(Test-Path $targetFileName)) -or ($Output.OverwriteExisting -eq 1 -and (Test-Path $targetFileName))) {	
																		$warning=$true
																	}
																}
															}
															
															if ($warning -eq $false) {
																foreach($entry in $zipFileDownload.Entries) {
																	if ($Output.TestPS -eq 1 -and (  ((($entry.FullName.Split(".")[-1].ToUpper()) -eq "PS1") -and $entry.FullName.Split(".").count -gt 1) -or ($Output.ReplaceExistingRunFile -eq 1 -and ($Output.RunFile) -and $Output.RunFile -like $Entry.FullName.Replace("/","\")  ) ) ) {
																		$entryStream = New-Object System.IO.StreamReader($entry.Open())
																		$inputScript = $entryStream.ReadToEnd()
																		$Errors = @()
																		if ($inputScript) {
																			[void][System.Management.Automation.Language.Parser]::ParseInput($inputScript,[ref]$null,[ref]$Errors)
																			if($Errors.Count -gt 0){
																				$warning=$true
																			}
																		}
																		if (!($inputScript)) {
																			$warning=$true
																		}
																	}
																}
																											
																if ($warning -eq $false) {
																	write-host "AutoUpdate: Downloaded files are being extracted" -ForegroundColor white
																	write-host
																	Write-EventLog –LogName Application –Source $AppLogName –EntryType Information –EventID ($AppLogBaseIndex+31) -Category $AppLogCategory -Message ("$Caption - $ProgramName Version: $Version`r`nChannel / PatchLevel: $Channel / $PatchLevel`r`nPID: $Pid`r`n`r`nAutoUpdate: Downloaded files are being extracted")
																	$Information += "AutoUpdate: Downloaded files are being extracted"
																	if ($DetailedLoggingEnabled -eq $true) {
																		Add-Content -Path "$LogFile" -Value "`r`nAutoUpdate: Downloaded files are being extracted" -Encoding Unicode
																	}
																
																	[string]$RunFile = $MyFolder + $Output.Runfile
																	foreach($entry in $zipFileDownload.Entries) {
																		if ($Output.ReplaceExistingRunFile -eq 1 -and ($Output.Runfile) -and $entry.FullName.Replace("/","\") -like $Output.Runfile) {
																			[string]$RunFile = $MyName
																			[string]$targetFileName = $MyName
																		} else {
																		
																			$DirCount = $entry.FullName.Split("/").Count
																			if ($DirCount -gt 1) {
																				$OutPath=""
																				for ($a=0; $a -lt ($DirCount-1); $a++) {

																					$OutPath=$OutPath+$entry.FullName.Split("/")[$a]

																					if (!(Test-Path ($MyFolder+$OutPath))) {
																						New-Item ($MyFolder+$OutPath) -Type Directory | Out-Null
																					}
																					$OutPath=$OutPath+ "\"
																				}
																				#[string]$targetFileName = $MyFolder + $OutPath + "\" + $entry.FullName.Split("/")[-1]
																				if ($entry.FullName.Split("/")[-1]) {
																					[string]$targetFileName = $MyFolder + $OutPath + $entry.FullName.Split("/")[-1]
																				} else {
																					[string]$targetFileName =""
																				}
																			} else {
																				[string]$targetFileName = $MyFolder + $entry.FullName
																			}
																			if ($Output.OverwriteExisting -eq 3 -and  (Test-Path $targetFileName)) {
																				[string]$targetFileName =""
																			}
																			#[string]$targetFileName = $MyFolder + $entry.FullName
																			
																		}
																		
																		#if ((($Output.OverwriteExisting -ne 2 -and $Output.OverwriteExisting -ne 1)) -or ($Output.OverwriteExisting -eq 2 -and (Test-Path $targetFileName)) -or ($Output.OverwriteExisting -eq 1 -and !(Test-Path $targetFileName))) {	
																		if ($targetFileName) {
																			if (Test-Path $targetFileName) {
																				Remove-Item $targetFileName -Force | out-null
																			}
																			$targetFileStream = $(New-Item -Path $targetFileName -ItemType File -Force).OpenWrite()
																			$entryStream = $entry.Open()
																			$entryStream.CopyTo($targetFileStream)
																		}
																		$targetFileStream,$entryStream | ForEach-Object Dispose
																		
																		#} else {
																		#	if ($Output.ReplaceExistingRunFile -eq 1 -and ($Output.Runfile) -and $entry.FullName -like $Output.Runfile) {
																		#		$RunFile = $null
																		#	}
																		
																	}
																} else {
																	write-host "WARNING: AutoUpdate failed as there is a compilation problem with $FileName" -ForegroundColor Yellow
																	write-host
																	Write-EventLog –LogName Application –Source $AppLogName –EntryType Warning –EventID ($AppLogBaseIndex+32) -Category $AppLogCategory -Message ("$Caption - $ProgramName Version: $Version`r`nChannel / PatchLevel: $Channel / $PatchLevel`r`nPID: $Pid`r`n`r`nWARNING: AutoUpdate failed as there is a compilation problem with $FileName")
																	$Warnings += "- AutoUpdate failed as there is a compilation problem with $FileName"
																	if ($DetailedLoggingEnabled -eq $true) {
																		Add-Content -Path "$LogFile" -Value "`r`nWARNING: AutoUpdate failed as there is a compilation problem with $FileName" -Encoding Unicode
																	}
																}
															} else {
																if ($Output.OverwriteExisting -eq 2) {
																	write-host "WARNING: AutoUpdate failed as one of the required target files was missing" -ForegroundColor Yellow
																	write-host
																	Write-EventLog –LogName Application –Source $AppLogName –EntryType Warning –EventID ($AppLogBaseIndex+33) -Category $AppLogCategory -Message ("$Caption - $ProgramName Version: $Version`r`nChannel / PatchLevel: $Channel / $PatchLevel`r`nPID: $Pid`r`n`r`nWARNING: AutoUpdate failed as one of the required target files was missing")
																	$Warnings += "- AutoUpdate failed as one of the required target files was missing"
																	if ($DetailedLoggingEnabled -eq $true) {
																		Add-Content -Path "$LogFile" -Value "`r`nWARNING: AutoUpdate failed as one of the required target files was missing" -Encoding Unicode
																	}							
																} else {
																	write-host "WARNING: AutoUpdate failed as one of the target files already exists" -ForegroundColor Yellow
																	write-host
																	Write-EventLog –LogName Application –Source $AppLogName –EntryType Warning –EventID ($AppLogBaseIndex+34) -Category $AppLogCategory -Message ("$Caption - $ProgramName Version: $Version`r`nChannel / PatchLevel: $Channel / $PatchLevel`r`nPID: $Pid`r`n`r`nAutoUpdate failed as one of the target files already exists")
																	$Warnings += "- AutoUpdate failed as one of the target files already exists"
																	if ($DetailedLoggingEnabled -eq $true) {
																		Add-Content -Path "$LogFile" -Value "`r`nWARNING: AutoUpdate failed as one of the target files already exists" -Encoding Unicode
																	}
																}
															}

															if($zipFileDownload) {
																$zipFileDownload.Dispose()
															}
															Remove-item $TempFile -force | out-null
														} else {
															write-host "WARNING: AutoUpdate failed as the download target $TempFile could not be opened" -ForegroundColor Yellow
															write-host
															Write-EventLog –LogName Application –Source $AppLogName –EntryType Warning –EventID ($AppLogBaseIndex+35) -Category $AppLogCategory -Message ("$Caption - $ProgramName Version: $Version`r`nChannel / PatchLevel: $Channel / $PatchLevel`r`nPID: $Pid`r`n`r`nWARNING: AutoUpdate failed as the download target $TempFile could not be opened")
															$Warnings += "- AutoUpdate failed as the download target $TempFile could not be opened"
															if ($DetailedLoggingEnabled -eq $true) {
																Add-Content -Path "$LogFile" -Value "`r`nWARNING: AutoUpdate failed as the download target $TempFile could not be opened" -Encoding Unicode
															}
															if (Test-Path $targetFileName) {
																Remove-Item $targetFileName -Force | out-null
															}
														}
													} else {
														if ($Output.TestPS -eq 1 -and (((($FileName.Split(".")[-1].ToUpper()) -eq "PS1") -and $FileName.Split(".").count -gt 1) -or $Output.ReplaceExistingRunFile -eq 1)) {
															#$inputScript=get-content $TempFile          # not reliable with language parser
															$inputScript=[System.IO.File]::ReadAllText( $TempFile )
															if ($inputScript) {
																$Errors = @()
																[void][System.Management.Automation.Language.Parser]::ParseInput($inputScript,[ref]$null,[ref]$Errors)
																if($Errors.Count -gt 0){
																	$warning=$true
																}
															}
															if (!$inputScript) {
																$warning=$true
															}
														}
														if ($warning -eq $false) {
															if ($Output.ReplaceExistingRunFile -eq 1 -and ($Output.Runfile)) {
																if (Test-Path $MyName) {
																	Remove-Item $MyName -Force | out-null
																}
																Rename-Item -Path $TempFile -newname $MyName
																[string]$RunFile = $MyName
															} else {
																if ($Output.Runfile) {
																	[string]$targetFileName = $MyFolder + $Output.Runfile
																} else {
																	[string]$targetFileName = $MyFolder + $FileName
																}
																if ($Output.OverwriteExisting -ne 3 -or !(Test-Path $targetFileName)) {
																	if (Test-Path $targetFileName) {
																		Remove-Item $targetFileName -Force | out-null
																	}
																	Rename-Item -Path $TempFile -newname $targetFileName
																	[string]$RunFile = $targetFileName
																} else {
																	$NoUpdate=$true
																	Remove-Item $TempFile -Force | out-null
																}
															}
														} else {
															write-host "WARNING: AutoUpdate failed as there is a compilation problem with one of the files in the package" -ForegroundColor Yellow
															write-host
															Write-EventLog –LogName Application –Source $AppLogName –EntryType Warning –EventID ($AppLogBaseIndex+36) -Category $AppLogCategory -Message ("$Caption - $ProgramName Version: $Version`r`nChannel / PatchLevel: $Channel / $PatchLevel`r`nPID: $Pid`r`n`r`nWARNING: AutoUpdate failed as there is a compilation problem with one of the files in the package")
															$Warnings += "- AutoUpdate failed as there is a compilation problem with one of the files in the package"
															if ($DetailedLoggingEnabled -eq $true) {
																Add-Content -Path "$LogFile" -Value "`r`nWARNING: AutoUpdate failed as there is a compilation problem with one of the files in the package" -Encoding Unicode
															}
														}
													}

													if ($warning -eq $false) {
														if ($Output.PatchLevel) {
															Set-RegistryValue -Path "$regalt\Versions\$version" -Name 'PatchLevel' -ValueType DWORD -Value $Output.PatchLevel
														}
														if (($Output.Runfile -and $RunFile)) {
															if (Test-Path $RunFile) {

																if (($output.UpdateModulesOnUpdate -eq 1) -and ($inifile.PowerShell.DisableRemoteModuleUpdateRequests -ne $True -and $inifile.PowerShell.DisableRemoteModuleUpdateRequests -ne 1)) {
																	Set-RegistryValue -Path "$regalt\Global Information" -Name 'CurrentMonth' -ValueType DWORD -Value 0
																}
																$string="(none)"
																if ($Output.PatchLevel) {
																	[string]$String=$Output.PatchLevel
																}
																if ($Output.AutoUpdate_NewVersion) {
																	write-host "AutoUpdate: STEPER is being updated to version v$($Output.AutoUpdate_NewVersion) - Patch Level: $string - $RunFile will be executed"
																	write-host
																	Write-EventLog –LogName Application –Source $AppLogName –EntryType Information –EventID ($AppLogBaseIndex+30) -Category $AppLogCategory -Message ("$Caption - $ProgramName Version: $Version`r`nChannel / PatchLevel: $Channel / $PatchLevel`r`nPID: $Pid`r`n`r`nSTEPER is being updated to version v$($Output.AutoUpdate_NewVersion) - Patch Level: $string - $RunFile will be executed`r`nDownloaded Package Hash: $Updatehash`r`nRunfile: $RunFile")
																	$Information += "- AutoUpdate: STEPER is being updated to version v$($Output.AutoUpdate_NewVersion) - Patch Level: $string - $RunFile will be executed"
																	if ($DetailedLoggingEnabled -eq $true) {
																		Add-Content -Path "$LogFile" -Value "`r`nAutoUpdate: STEPER is being updated to version v$($Output.AutoUpdate_NewVersion) - Patch Level: $string - $RunFile will be executed" -Encoding Unicode
																	}
																} else {
																	write-host "AutoUpdate: STEPER is being updated - Patch Level: $string - $RunFile will be executed"
																	write-host
																	Write-EventLog –LogName Application –Source $AppLogName –EntryType Information –EventID ($AppLogBaseIndex+30) -Category $AppLogCategory -Message ("$Caption - $ProgramName Version: $Version`r`nChannel / PatchLevel: $Channel / $PatchLevel`r`nPID: $Pid`r`n`r`nSTEPER is being updated - Patch Level: $string - $RunFile will be executed`r`nDownloaded Package Hash: $Updatehash`r`nRunfile: $RunFile")
																	$Information += "- AutoUpdate: STEPER is being updated - Patch Level: $string - $RunFile will be executed"
																	if ($DetailedLoggingEnabled -eq $true) {
																		Add-Content -Path "$LogFile" -Value "`r`nAutoUpdate: STEPER is being updated - Patch Level: $string - $RunFile will be executed" -Encoding Unicode
																	}
																}
																#$MyInvocation | fl *
																#[string]$RunFile = $MyFolder + "t 3.ps1"       

																#powershell.exe -executionpolicy bypass "$RunFile" -ArgumentList @PSBoundParameters $MyInvocation.UnBoundArguments # doesnt work

																& $Runfile @PSBoundParameters $MyInvocation.UnBoundArguments
																if (($output.ExitControl -eq 0) -or ($output.ExitControl -eq $null)) {
																	exit 0
																}
															} else {
																write-host "WARNING: AutoUpdate failed as the specified command file is missing: $RunFile" -ForegroundColor Yellow
																write-host
																Write-EventLog –LogName Application –Source $AppLogName –EntryType Warning –EventID ($AppLogBaseIndex+37) -Category $AppLogCategory -Message ("$Caption - $ProgramName Version: $Version`r`nChannel / PatchLevel: $Channel / $PatchLevel`r`nPID: $Pid`r`n`r`nWARNING: AutoUpdate failed as the specified command file is mising: $RunFile")
																$Warnings += "- AutoUpdate failed as the specified command file is missing: $RunFile"
																if ($DetailedLoggingEnabled -eq $true) {
																	Add-Content -Path "$LogFile" -Value "`r`nWARNING: AutoUpdate failed as the specified command file is missing: $RunFile" -Encoding Unicode
																}
															}
														}
														if ($output.ExitControl -eq 1) {
															write-host "AutoUpdate: STEPER is being restarted" -ForegroundColor white
															write-host
															Write-EventLog –LogName Application –Source $AppLogName –EntryType Information –EventID ($AppLogBaseIndex+38) -Category $AppLogCategory -Message ("$Caption - $ProgramName Version: $Version`r`nChannel / PatchLevel: $Channel / $PatchLevel`r`nPID: $Pid`r`n`r`nAutoUpdate: STEPER is being restarted")
															$Information += "- AutoUpdate has downloaded an update file, and STEPER is being restarted"
															if ($DetailedLoggingEnabled -eq $true) {
																Add-Content -Path "$LogFile" -Value "`r`nAutoUpdate: STEPER is being restarted" -Encoding Unicode
															}
															$RunFile = $MyName
															& $MyName @PSBoundParameters $MyInvocation.UnBoundArguments
															exit 0
														}


														if (!($Output.Runfile -and $RunFile)) {
															if ($NoUpdate -eq $false) {
																if ($targetFileName) {
																	write-host "AutoUpdate has downloaded an update file including $targetFileName" -ForegroundColor white
																	write-host
																	Write-EventLog –LogName Application –Source $AppLogName –EntryType Information –EventID ($AppLogBaseIndex+39) -Category $AppLogCategory -Message ("$Caption - $ProgramName Version: $Version`r`nChannel / PatchLevel: $Channel / $PatchLevel`r`nPID: $Pid`r`n`r`nAutoUpdate has downloaded an update file including $targetFileName")
																	$Information += "- AutoUpdate has downloaded an update file including $targetFileName"
																	$Actions += "- Applied an update to STEPER via AutoUpdate"
																	if ($DetailedLoggingEnabled -eq $true) {
																		Add-Content -Path "$LogFile" -Value "`r`nAutoUpdate has downloaded an update file including $targetFileName" -Encoding Unicode
																	}
																} else {
																	write-host "AutoUpdate has downloaded an update file" -ForegroundColor white
																	write-host
																	Write-EventLog –LogName Application –Source $AppLogName –EntryType Information –EventID ($AppLogBaseIndex+39) -Category $AppLogCategory -Message ("$Caption - $ProgramName Version: $Version`r`nChannel / PatchLevel: $Channel / $PatchLevel`r`nPID: $Pid`r`n`r`nAutoUpdate has downloaded an update file")
																	$Information += "- AutoUpdate has downloaded an update file"
																	$Actions += "- Applied an update to STEPER via AutoUpdate"
																	if ($DetailedLoggingEnabled -eq $true) {
																		Add-Content -Path "$LogFile" -Value "`r`nAutoUpdate has downloaded an update file" -Encoding Unicode
																	}
																}
																
															} else { # can only be true for a single file download thats been ignored due to $Output.OverwriteExisting -eq 3
																write-host "AutoUpdate downloaded $targetFileName but did not replace the existing file" -ForegroundColor white
																write-host
																Write-EventLog –LogName Application –Source $AppLogName –EntryType Information –EventID ($AppLogBaseIndex+39) -Category $AppLogCategory -Message ("$Caption - $ProgramName Version: $Version`r`nChannel / PatchLevel: $Channel / $PatchLevel`r`nPID: $Pid`r`n`r`nAutoUpdate downloaded $targetFileName but did not replace the existing file")
																$Information += "- AutoUpdate downloaded $targetFileName but did not replace the existing file"
																if ($DetailedLoggingEnabled -eq $true) {
																	Add-Content -Path "$LogFile" -Value "`r`nAutoUpdate downloaded $targetFileName but did not replace the existing file" -Encoding Unicode
																}
															}
														}
														
														if ($output.ExitControl -gt 1 -and $output.ExitControl -le 601) {
															Start-Sleep -s ($output.ExitControl -1)
														}
														
	
														
													} #else {
													#	write-host "WARNING: AutoUpdate unknown error" -ForegroundColor Yellow
													#	write-host
													#	Write-EventLog –LogName Application –Source $AppLogName –EntryType Warning –EventID ($AppLogBaseIndex+37) -Category $AppLogCategory -Message ("$Caption - $ProgramName Version: $Version`r`nChannel / PatchLevel: $Channel / $PatchLevel`r`nPID: $Pid`r`n`r`nWARNING: AutoUpdate unknown error")
													#	$Information += "- AutoUpdate unknown error"
													#	if ($DetailedLoggingEnabled -eq $true) {
													#		Add-Content -Path "$LogFile" -Value "`r`nWARNING: AutoUpdate unknown error" -Encoding Unicode
													#	}
													#}
												} else {
													write-host "WARNING: AutoUpdate failed as the downloaded file hash $Updatehash doesn't match" -ForegroundColor Yellow
													write-host
													Write-EventLog –LogName Application –Source $AppLogName –EntryType Warning –EventID ($AppLogBaseIndex+40) -Category $AppLogCategory -Message ("$Caption - $ProgramName Version: $Version`r`nChannel / PatchLevel: $Channel / $PatchLevel`r`n`r`AutoUpdate failed as there is a problem with the downloaded file hash - $Updatehash")
													$Warnings += "- AutoUpdate failed as there is a problem with the downloaded file hash - $Updatehash"
													if ($DetailedLoggingEnabled -eq $true) {
														Add-Content -Path "$LogFile" -Value "`r`nWARNING: AutoUpdate failed as there is a problem with the downloaded file hash - $Updatehash" -Encoding Unicode
													}
													Remove-item $TempFile -force
												}
											} else {
												write-host "WARNING: AutoUpdate failed as the download did not complete - $($Output.DownloadLink)" -ForegroundColor Yellow
												write-host
												Write-EventLog –LogName Application –Source $AppLogName –EntryType Warning –EventID ($AppLogBaseIndex+41) -Category $AppLogCategory -Message ("$Caption - $ProgramName Version: $Version`r`nChannel / PatchLevel: $Channel / $PatchLevel`r`nPID: $Pid`r`n`r`nWARNING: AutoUpdate failed as the download did not complete - $($Output.DownloadLink)")
												$Warnings += "- AutoUpdate failed as the download did not complete - $($Output.DownloadLink)"
												if ($DetailedLoggingEnabled -eq $true) {
													Add-Content -Path "$LogFile" -Value "`r`nWARNING: AutoUpdate failed as the download did not complete - $($Output.DownloadLink)" -Encoding Unicode
												}
											}
										} else {
											write-host "WARNING: AutoUpdate failed as the download target $TempFile could not be prepared" -ForegroundColor Yellow
											write-host
											Write-EventLog –LogName Application –Source $AppLogName –EntryType Warning –EventID ($AppLogBaseIndex+42) -Category $AppLogCategory -Message ("$Caption - $ProgramName Version: $Version`r`nChannel / PatchLevel: $Channel / $PatchLevel`r`nPID: $Pid`r`n`r`nWARNING: AutoUpdate failed as the download target $TempFile could not be prepared")
											$Warnings += "- AutoUpdate failed as the download target $TempFile could not be prepared"
											if ($DetailedLoggingEnabled -eq $true) {
												Add-Content -Path "$LogFile" -Value "`r`nWARNING: AutoUpdate failed as the download target $TempFile could not be prepared" -Encoding Unicode
											}
										}
									} else {
										write-host "WARNING: AutoUpdate failed as the download will not be able to be executed" -ForegroundColor Yellow
										write-host
										Write-EventLog –LogName Application –Source $AppLogName –EntryType Warning –EventID ($AppLogBaseIndex+43) -Category $AppLogCategory -Message ("$Caption - $ProgramName Version: $Version`r`nChannel / PatchLevel: $Channel / $PatchLevel`r`nPID: $Pid`r`n`r`nWARNING: AutoUpdate failed as the download will not be able to be executed")
										$Warnings += "- AutoUpdate failed as the download target $TempFile could not be prepared"
										if ($DetailedLoggingEnabled -eq $true) {
											Add-Content -Path "$LogFile" -Value "`r`nWARNING: AutoUpdate failed as the download will not be able to be executed" -Encoding Unicode
										}
									}										

								} else {
									if ($Output.OverwriteExisting -eq 2) {
										write-host "WARNING: AutoUpdate failed as the required target file $FileName was missing" -ForegroundColor Yellow
										write-host
										Write-EventLog –LogName Application –Source $AppLogName –EntryType Warning –EventID ($AppLogBaseIndex+44) -Category $AppLogCategory -Message ("$Caption - $ProgramName Version: $Version`r`nChannel / PatchLevel: $Channel / $PatchLevel`r`nPID: $Pid`r`n`r`nWARNING: AutoUpdate failed as the required target file $FileName was missing")
										$Warnings += "- AutoUpdate failed as the required target file $FileName was missing"
										if ($DetailedLoggingEnabled -eq $true) {
											Add-Content -Path "$LogFile" -Value "`r`nWARNING: AutoUpdate failed as the required target file $FileName was missing" -Encoding Unicode
										}							
									} else {
										write-host "WARNING: AutoUpdate failed as the target file $FileName already exists" -ForegroundColor Yellow
										write-host
										Write-EventLog –LogName Application –Source $AppLogName –EntryType Warning –EventID ($AppLogBaseIndex+45) -Category $AppLogCategory -Message ("$Caption - $ProgramName Version: $Version`r`nChannel / PatchLevel: $Channel / $PatchLevel`r`nPID: $Pid`r`n`r`nWARNING: AutoUpdate failed as the target file $FileName already exists")
										$Warnings += "- AutoUpdate failed as the target file $FileName already exists"
										if ($DetailedLoggingEnabled -eq $true) {
											Add-Content -Path "$LogFile" -Value "`r`nWARNING: AutoUpdate failed as the target file $FileName already exists" -Encoding Unicode
										}
									}
								}
							} else {
								write-host "WARNING: AutoUpdate failed as there is a problem with the download link" -ForegroundColor Yellow
								write-host
								Write-EventLog –LogName Application –Source $AppLogName –EntryType Warning –EventID ($AppLogBaseIndex+46) -Category $AppLogCategory -Message ("$Caption - $ProgramName Version: $Versiong`r`nChannel / PatchLevel: $Channel / $PatchLevel`r`nPID: $Pid`r`n`r`nWARNING: AutoUpdate failed as there is a problem with the download link")
								$Warnings += "- AutoUpdate failed as there is a problem with the download link"
								if ($DetailedLoggingEnabled -eq $true) {
									Add-Content -Path "$LogFile" -Value "`r`nWARNING: AutoUpdate failed as there is a problem with the download link" -Encoding Unicode
								}
							}
						} else {
							write-host "WARNING: AutoUpdate failed - this instance of STEPER could not be updated as STEPER has been modified" -ForegroundColor Yellow
							write-host
							Write-EventLog –LogName Application –Source $AppLogName –EntryType Warning –EventID ($AppLogBaseIndex+47) -Category $AppLogCategory -Message ("$Caption - $ProgramName Version: $Version`r`nChannel / PatchLevel: $Channel / $PatchLevel`r`nPID: $Pid`r`n`r`nWARNING: AutoUpdate failed - this instance of STEPER could not be updated as STEPER has been modified")
							$Warnings += "- AutoUpdate failed - this instance of STEPER could not be updated as STEPER has been modified"
							if ($DetailedLoggingEnabled -eq $true) {
								Add-Content -Path "$LogFile" -Value "`r`nWARNING: AutoUpdate failed - this instance of STEPER could not be updated as STEPER has been modified" -Encoding Unicode
							}
						}
					} else {
						#write-host "AutoUpdate: No update is available" -ForegroundColor white
						#write-host
						$Information += "- AutoUpdate: No update to the current patch level $Patchlevel is available"
						if ($DetailedLoggingEnabled -eq $true) {
							Add-Content -Path "$LogFile" -Value "`r`nAutoUpdate: No update to the current patch level $Patchlevel is available" -Encoding Unicode
						}
						if ($Verbose -eq $true) {    
							Write-EventLog –LogName Application –Source $AppLogName –EntryType Information –EventID ($AppLogBaseIndex+48) -Category $AppLogCategory -Message ("$Caption - $ProgramName Version: $Version`r`nChannel / PatchLevel: $Channel / $PatchLevel`r`n`r`AutoUpdate: No update to the current patch level $Patchlevel is available")
						}
					}
				} else {
					if ($Output.AutoUpdate -eq 1) {
						write-host "AutoUpdate: A STEPER AutoUpdate is available, however AutoUpdate has been disabled in the settings file" -ForegroundColor white
						write-host
						Write-EventLog –LogName Application –Source $AppLogName –EntryType Information –EventID ($AppLogBaseIndex+49) -Category $AppLogCategory -Message ("$Caption - $ProgramName Version: $Version`r`nChannel / PatchLevel: $Channel / $PatchLevel`r`n`r`AutoUpdate: A STEPER AutoUpdate is available, however AutoUpdate has been disabled in the settings file")
						$Information += "- AutoUpdate: A STEPER AutoUpdate is available, however AutoUpdate has been disabled in the settings file"
						if ($DetailedLoggingEnabled -eq $true) {
							Add-Content -Path "$LogFile" -Value "`r`nAutoUpdate: A STEPER AutoUpdate is available, however AutoUpdate has been disabled in the settings file" -Encoding Unicode
						}
					} else {
						#write-host "AutoUpdate: No update is available" -ForegroundColor white
						#write-host
						$Information += "- AutoUpdate: No update is available"
						if ($DetailedLoggingEnabled -eq $true) {
							Add-Content -Path "$LogFile" -Value "`r`nAutoUpdate: No update is available" -Encoding Unicode
						}
						if ($Verbose -eq $true) {    
							Write-EventLog –LogName Application –Source $AppLogName –EntryType Information –EventID ($AppLogBaseIndex+50) -Category $AppLogCategory -Message ("$Caption - $ProgramName Version: $Version`r`nChannel / PatchLevel: $Channel / $PatchLevel`r`n`r`AutoUpdate: No update is available")
						}
					}
				}

			}

            $output = @()
			$output = @($history | where {([int]$_.Version_Major -gt [int]$VersionMajor -or ([int]$_.Version_Major -eq [int]$VersionMajor -and [int]$_.Version_Minor -gt [int]$VersionMinor ) -and [int]$_.Channel -eq [int]$Channel) -or ([int]$_.Version_Major -eq [int]$VersionMajor -and [int]$_.Version_Minor -eq [int]$VersionMinor -and [int]$_.Version_Trivial -gt [int]$VersionTrivial -and [int]$_.Channel -eq [int]$Channel)})

			$string = @()
			$string = "version"
            $ReleaseString = $null
            if ( @($output).count -gt 0) {
                $ReleaseString = ($Output | Select -Last 1 ).Change_Notes
            }

			if ( @($output).count -gt 1) {
                $string = "versions"
            }

			if ( @($output).count -gt 0) {
				if ( @($output | where {[int]$_.MajorUpdate -eq 1 }).Count -gt 0) {
					$UpdatesAvailable += "- There is a major new version available - this version of STEPER ($Version) is $(@($output).count) $string out of date - the latest version is v$($output[$output.count-1].Version_Major).$($output[$output.count-1].Version_Minor).$($output[$output.count-1].Version_Trivial) dated $($output[$output.count-1].Date)"
                    write-host "WARNING: There is a major new version available - this version of STEPER ($Version) is $(@($output).count) $string out of date" -Foregroundcolor red
					write-host "The latest version is v$($output[$output.count-1].Version_Major).$($output[$output.count-1].Version_Minor).$($output[$output.count-1].Version_Trivial) dated $($output[$output.count-1].Date)" -Foregroundcolor yellow
                    if ($ReleaseString) {
                        $UpdatesAvailable += "- See the latest release notes at $ReleaseString"
                        write-host "See the latest release notes at $ReleaseString" -Foregroundcolor yellow
                        }
                    write-host

                    $Warnings += $UpdatesAvailable
				} else {
					$UpdatesAvailable += "- This version of STEPER ($Version) is $(@($output).count) $string out of date - the latest version is v$($output[$output.count-1].Version_Major).$($output[$output.count-1].Version_Minor).$($output[$output.count-1].Version_Trivial) dated $($output[$output.count-1].Date)"
                    write-host "NOTE: This version of STEPER ($Version) is $(@($output).count) $string out of date" -Foregroundcolor yellow
					write-host "The latest version is v$($output[$output.count-1].Version_Major).$($output[$output.count-1].Version_Minor).$($output[$output.count-1].Version_Trivial) dated $($output[$output.count-1].Date)" -Foregroundcolor white
                    if ($ReleaseString) {
                        $UpdatesAvailable += "- See the latest release notes at $ReleaseString"
                        write-host "- See the latest release notes at $ReleaseString" -Foregroundcolor white
                    }
                    write-host

                    $Information += $UpdatesAvailable
				}
			}
            $PreviouslyKilled = (Get-RegistryValue -Path "$regalt\Versions\$version" -Name 'Killed' -ValueType DWORD )
            if ($PreviouslyKilled -eq 1) {
                $UpdatesAvailable += "- This version of STEPER has been disabled - please update this program"
                write-host "ERROR: This version of STEPER has been disabled - please update this program" -Foregroundcolor red
                write-host
                $MajorErrors += "- This version of STEPER has been disabled - please update this program"
                Write-EventLog –LogName Application –Source $AppLogName –EntryType Error –EventID ($AppLogBaseIndex+20) -Category $AppLogCategory -Message ("$Caption - $ProgramName Version: $Version - $String`r`nChannel / PatchLevel: $Channel / $PatchLevel`r`nPID: $Pid`r`n`r`nERROR: This version of STEPER has been disabled - please update this program")
            }
			$Actions += "- AutoUpdate: Update check complete"
			if ($UpdatesAvailable.Count -eq 0) {
                write-host "AutoUpdate: Update check complete" -Foregroundcolor green
                write-host				
			}
			$Obj = @()
		} else {
            $UpdatesAvailable += "- Unable to check for updates, please ensure that https://kbftp.stonegroup.co.uk/ is not blocked"
            write-host "WARNING: Unable to check for updates, please ensure that https://kbftp.stonegroup.co.uk/ is not blocked" -foregroundcolor red
            $Warnings += $UpdatesAvailable
            write-host
        }
	}
}

$OLHash = (Get-RegistryValue -Path "$regalt\Versions\$version" -Name 'OLHash' -ValueType String )
if ($OLHash) {
	if ($OLHash -ne $hash -and $hash -ne "(not calculated)" ) {
		write-host "WARNING: STEPER has detected a code change. Changes to the STEPER code are completely unsupported" -foregroundcolor red
		$Warnings += "- STEPER has detected a code change. Changes to the STEPER code are completely unsupported"
		write-host
	}
}

$MotdType = (Get-RegistryValue -Path "$regalt\Versions\$version" -Name 'MotdType' -ValueType DWORD )
if ($MotdType -eq 1 -or $MotdType -eq 2 -or $MotdType -eq 3) {
	$Motd = (Get-RegistryValue -Path "$regalt\Versions\$version" -Name 'Motd' -ValueType String )
}
if ($Motd -and $Motd -ne "(no message)") {

    write-host "$screenstring" -foregroundcolor green
	write-host

	if ($MotdType -eq 1) {
		write-host "$Motd" -foregroundcolor white
		$Information += "- MOTD: $Motd"
		write-host
	}

	if ($MotdType -eq 2) {
		write-host "WARNING: $Motd" -foregroundcolor yellow
		$Warnings += "- MOTD: $Motd"
		write-host
	}

	if ($MotdType -eq 3) {
		write-host "ERROR: $Motd" -foregroundcolor red
		$MajorErrors += "- MOTD: $Motd"
		write-host
	}
}

if ($iniFile.General.MakeChanges -eq $true -or $iniFile.General.MakeChanges -eq 1) {
	$MakeChanges = $true
}

if ($WhatIf -eq $true) { # whatif support without using [CmdletBinding(SupportsShouldProcess=$true)] as its just not reliable for using with verbose
	if ($MakeChanges -eq $true) {
		$OtherSettings += "- WhatIF Specified on Command Line: MakeChanges forced to False" # this exact string is referenced later
	}
	$MakeChanges = $false
}

if ($iniFile.Override.ApplytoAllTeachers -eq $true -or $iniFile.Override.ApplytoAllTeachers -eq 1) {
	$ApplytoAllTeachers = $true
}

if ($iniFile.Override.ApplytoAllStudents -eq $true -or $iniFile.Override.ApplytoAllStudents -eq 1) {
	$ApplytoAllStudents = $true
}
[int]$GlobalOnFailureApplytoAllUsersOnNextRun=3
if ($iniFile.Override.GlobalOnFailureApplytoAllUsersOnNextRun) {
	$GlobalOnFailureApplytoAllUsersOnNextRun = (@(if ($iniFile.Override.GlobalOnFailureApplytoAllUsersOnNextRun -ne $null) { if ($iniFile.Override.GlobalOnFailureApplytoAllUsersOnNextRun.Trim($TrimChar) -ne $null -and $iniFile.Override.GlobalOnFailureApplytoAllUsersOnNextRun.Trim($TrimChar) -ne "") { @(iex "echo $($iniFile.Override.GlobalOnFailureApplytoAllUsersOnNextRun.Trim($TrimChar))") | select -Unique | % { switch($_){0 {[int]-99}; "False" {[int]-99};"Functions" {[int]2}; "Function" {[int]2}; "F" {[int]2}; 2 {[int]2}; "Error" {[int]4}; "Errors" {[int]4}; 4 {[int]4}; 6 {[int]2,[int]4}; 1 {[int]2,[int]4}; "True" {[int]2,[int]4}; "Warning" {[int]8}; "Warnings" {[int]8}; "W" {[int]8}; 8 {[int]8}; 10 {[int]2,[int]8}; 12 {[int]4,[int]8}; "All" {[int]14}; default {[int]0}} } }  else { [int]6 } } else { [int]6 } ) | select -Unique | select -first 10 | Measure-Object -sum).sum | % {if ($_ -gt 14) { 14 } else { if ($_ -lt 0) { 0 } else { $_ } }}
}

if ($regpath) {
	if ((Get-RegistryValue -Path $regpath -Name 'ApplytoAllTeachersOnNextRun' -ValueType DWORD ) -eq 1) {
		#Remove-ItemProperty -Path $regpath -Name 'ApplytoAllTeachersOnNextRun' -Force -Erroraction SilentlyContinue # remove later
		$OtherSettings += "- ApplytoAllTeachersOnNextRun requested from previous run via registry - User Problem Resolution via User Re-Processing"
		$ApplytoAllTeachers = $true
	}

	if ((Get-RegistryValue -Path $regpath -Name 'ApplytoAllStudentsOnNextRun' -ValueType DWORD ) -eq 1) {
		#Remove-ItemProperty -Path $regpath -Name 'ApplytoAllStudentsOnNextRun' -Force -Erroraction SilentlyContinue # remove later
		$OtherSettings += "- ApplytoAllStudentsOnNextRun requested from previous run via registry - User Problem Resolution via User Re-Processing "
		$ApplytoAllStudents = $true
	}
}

if ($MajorErrors.Count -eq 0) {

    write-host "$screenstring" -foregroundcolor green
	write-host

        # Record Startup Info

    if ((Get-RegistryValue -Path $regpath -Name 'CurrentlyRunning' -ValueType DWORD ) -eq 1) {
        $previousruntime=(Get-RegistryValue -Path $regpath -Name 'StartTime' -ValueType String)
        $previousrunminutes =(New-TimeSpan -Start $previousruntime -End $starttime).TotalMinutes
        $previousrunminutes = [math]::round($previousrunminutes,1)

        if ($iniFile.Misc.DontRunIfSteperMayBeRunningTimeOutinMinutes) {
            [int]$PreviousRunTimeout=$iniFile.Misc.DontRunIfSteperMayBeRunningTimeOutinMinutes -as [int]
            if ($PreviousRunTimeout -lt 0 -or $PreviousRunTimeout -gt 4320) { $PreviousRunTimeout = 240 }
            if ($previousruntime -and $PreviousRunTimeout -gt 0) {
                if ($previousrunminutes -lt $PreviousRunTimeout) {
                    $MajorErrors +=  "- The previous run of STEPER that started at $previousruntime did not complete. As the timeout period of $PreviousRunTimeout minutes has not expired, STEPER will terminate"
                    write-Host "ERROR: The previous run of STEPER that started at $previousruntime did not complete. As the timeout period of $PreviousRunTimeout minutes has not expired, STEPER will terminate" -foreground red
                    write-host "Please ensure that if you are using multiple instances of STEPER at the same time that you use different profile names for each instance" -ForegroundColor Yellow
                    write-host
                    if ($DetailedLoggingEnabled -eq $true) {
                        Add-Content -Path "$LogFile" -Value "ERROR: The previous run of STEPER that started at $previousruntime did not complete. As the timeout period of $PreviousRunTimeout minutes has not expired, STEPER will terminate" -Encoding Unicode
                        Add-Content -Path "$LogFile" -Value "Please ensure that if you are using multiple instances of STEPER at the same time that you use different profile names for each instance" -Encoding Unicode
                    }
                    $DontRecordCompletionTime=$true
                } else {
                    $Warnings +=  "- The previous run of STEPER that started at $previousruntime did not complete. As the timeout period of $PreviousRunTimeout minutes has expired, STEPER will continue"
                    write-Host "WARNING: The previous run of STEPER that started at $previousruntime did not complete. As the timeout period of $PreviousRunTimeout minutes has expired, STEPER will continue" -foreground red
                    write-host "Please ensure that if you are using multiple instances of STEPER at the same time that you use different profile names for each instance" -ForegroundColor Yellow
                    write-host
                    if ($DetailedLoggingEnabled -eq $true) {
                        Add-Content -Path "$LogFile" -Value "The previous run of STEPER that started at $previousruntime did not complete. As the timeout period of $PreviousRunTimeout minutes has expired, STEPER will continue" -Encoding Unicode
                        Add-Content -Path "$LogFile" -Value "Please ensure that if you are using multiple instances of STEPER at the same time that you use different profile names for each instance" -Encoding Unicode
                    }
                    $PreRunPause=$true
                }
            } else {
                $Warnings += "- The previous run of STEPER that started at $previousruntime did not complete"
                write-Host "WARNING: The previous run of STEPER that started at $previousruntime did not complete" -foreground red
                write-host "Please ensure that if you are using multiple instances of STEPER at the same time that you use different profile names for each instance" -ForegroundColor Yellow
                write-host
                if ($DetailedLoggingEnabled -eq $true) {
                    Add-Content -Path "$LogFile" -Value "The previous run of STEPER did not complete" -Encoding Unicode
                    Add-Content -Path "$LogFile" -Value "Please ensure that if you are using multiple instances of STEPER at the same time that you use different profile names for each instance" -Encoding Unicode
                }
                $PreRunPause=$true
            }
        } else {
            if ($previousruntime) {
                $Warnings += "- The previous run of STEPER that started at $previousruntime did not complete"
                write-Host "WARNING: The previous run of STEPER that started $previousruntime did not complete" -foreground red
                write-host "Please ensure that if you are using multiple instances of STEPER at the same time that you use different profile names for each instance" -ForegroundColor Yellow
                write-host
                if ($DetailedLoggingEnabled -eq $true) {
                    Add-Content -Path "$LogFile" -Value "The previous run of STEPER that started at $previousruntime did not complete" -Encoding Unicode
                    Add-Content -Path "$LogFile" -Value "Please ensure that if you are using multiple instances of STEPER at the same time that you use different profile names for each instance" -Encoding Unicode
                }
                $PreRunPause=$true
            } else {
                $Warnings += "- The previous run of STEPER did not complete"
                write-Host "WARNING: The previous run of STEPER did not completed" -foreground red
                write-host "Please ensure that if you are using multiple instances of STEPER at the same time that you use different profile names for each instance" -ForegroundColor Yellow
                write-host
                if ($DetailedLoggingEnabled -eq $true) {
                    Add-Content -Path "$LogFile" -Value "The previous run of STEPER did not complete" -Encoding Unicode
                    Add-Content -Path "$LogFile" -Value "Please ensure that if you are using multiple instances of STEPER at the same time that you use different profile names for each instance" -Encoding Unicode
                }
                $PreRunPause=$true
            }
        }
		if ( ($GlobalOnFailureApplytoAllUsersOnNextRun -band 4) -eq 4) {
			$ApplytoAllTeachers=$true
			$ApplytoAllStudents=$true
			write-host "Setting ApplytoAllTeachers / ApplytoAllStudents as the previous run of STEPER did not complete" -foregroundcolor yellow
			$OtherSettings += "- Setting ApplytoAllTeachers / ApplytoAllStudents as the previous run of STEPER did not complete"
		}	
	}
}


if ($MakeChanges -eq $true) {
    [string]$textcolour = "green"
    [string]$alttextcolour = "yellow"
} else {
    [string]$textcolour = "cyan"
    [string]$alttextcolour = "cyan"
}


Function Generate-Data {
Param(
    
    [Parameter(
        Mandatory=$True,
        Position=0,
        ValueFromPipeLine=$true
    )]
    [Alias("Path")]
    [String]$Reglocation
)
    try {
        [byte[]]$MasterKey = 98,106,104,52,55,118,97,106,103,51,103,118,98,50,107,56,50,122,56,49,104,98,51,118,117,104,52,55,102,110,99,109
        $PlainText = [string]((get-process).id | sort | select -last 1) + [string]($hostname + (Get-date).tostring("ddMMyyyyHHmmss") +[string]$pid)
        $secureString = Convertto-SecureString $PlainText -AsPlainText -Force
        $EncryptedString = ConvertFrom-SecureString -SecureString $secureString -Key $MasterKey
        if ($EncryptedString) {
            Set-RegistryValue -Path "$Reglocation" -Name 'Data' -ValueType String -Value $EncryptedString.Substring($EncryptedString.length-34,32)
            return $true
        } else {
            return $false
        }
    }
    Catch {
        return $false
    }
}


Function Decode-GAP {
Param(
    
    [Parameter(
        Mandatory=$True,
        Position=0,
        ValueFromPipeLine=$true
    )]
    [Alias("ProtectedPassword")]
    [String]$EncodedString,
    
    [Parameter(
        Mandatory=$True,
        Position=1
    )]
    [Alias("Key")]
    [string]$EncryptionKey
)
    Try{
        $enc = [system.Text.Encoding]::UTF8
        $ByteKey = $enc.GetBytes($EncryptionKey)

        $SecureString = ConvertTo-SecureString $EncodedString -Key $ByteKey
        $bstr = [Runtime.InteropServices.Marshal]::SecureStringToBSTR($SecureString)
        [string]$String = [Runtime.InteropServices.Marshal]::PtrToStringAuto($bstr)
        [Runtime.InteropServices.Marshal]::ZeroFreeBSTR($bstr)

        $data = @()
        [convert]::FromBase64String($String) | ForEach-Object{
            If($_ -ne 0){
                $data += [char]$_
            }
        }
        return ($data -join '')
    }
    catch {
        return $null
    }
}

Function Encode-GAP {
Param(
    
    [Parameter(
        Mandatory=$True,
        Position=0,
        ValueFromPipeLine=$true
    )]
    [String]$Password,

    [Parameter(
        Mandatory=$True,
        Position=1
    )]
    [Alias("Key")]
    [string]$EncryptionKey
)
    Try{
        $enc = [system.Text.Encoding]::UTF8
        $ByteKey = $enc.GetBytes($EncryptionKey)
        
        $bytes = [System.Text.Encoding]::Unicode.GetBytes($Password)
        $encodedCommand = [Convert]::ToBase64String($bytes)
        $secureString = Convertto-SecureString $encodedCommand -AsPlainText -Force
        $EncryptedString = ConvertFrom-SecureString -SecureString $secureString -Key $ByteKey

        return $EncryptedString
    }
    catch {
        return $null
    }
}

Function Update-SettingsPassword {
Param(
    
    [Parameter(
        Mandatory=$True,
        Position=0

    )]
    [Alias("SettingsFile")]
    [String]$FileName,

    [Parameter(
        Mandatory=$True,
        Position=1
    )]
    [Alias("Password")]
    [string]$EncryptedPassword
)
    Try{

        $filecontents = Get-Content $FileName
        if ($filecontents) {
            Remove-Item "$FileName"
            $fixed=$false
            for ($a=0;$a -lt $filecontents.count; $a++) {
                if ($filecontents[$a] -like [string]'`[*') {
                    $CredentialsSection=$false
                }
                if ($filecontents[$a] -like [string]'`[Credentials`]') {
                    $CredentialsSection=$true
                }
                $SkipthisEntry = $false

                if ($filecontents[$a] -like "GA_Password=*" -and $CredentialsSection -eq $true) {
                    $SkipthisEntry = $true
                    if ($fixed -eq $false) {
                        Add-Content -Path "$FileName" -Value "GA_Password=" -Encoding ascii
                        Add-Content -Path "$FileName" -Value "ProtectedGA_Password=$EncryptedPassword" -Encoding ascii
                        $fixed = $true
                    }
                }
                if ($filecontents[$a] -like "ProtectedGA_Password=*" -and $CredentialsSection -eq $true) {
                    $SkipthisEntry = $true
                }
				if ($SkipthisEntry -eq $false) {
					Add-Content -Path "$FileName" -Value $filecontents[$a] -Encoding ascii
				}
            }
        } else {
            return $false
        }
        return $true
    }
    catch {
        return $false
    }
}




Function NumericalListCreator {
# To be used for the Processing / Regular version of CollectionIDstoExclude

# Sample Input 4,6,3,1,1,A,8,9-11 as String
# Sample Output: 1,3,4,6,8,9,10,11 as Int Array


 #reference code 
 # [int[]]@(iex "echo $($String.Trim(","))" | select -Unique | where {$_ -is [int] -or $_ -eq 0 } | where { $_ -gt -1 -and $_ -le 10000} | sort )
    Param ($InputString)
	
	$Trimchar=' ,' + [char]0x009
	
    if ($InputString) {
		$InputString = $InputString.Replace(" -","-") # 1.0.94
        $InputString = $InputString.Replace("- ","-") # 1.0.94
        if ($InputString.Trim($TrimChar) -ne $null -and $InputString.Trim($TrimChar) -ne "") {
            $StringConc = @()
            $ListOutput = $null
            $SplitString = @(iex "echo $($InputString.Trim($TrimChar))")

            Foreach ($LittleString in $SplitString) {
                $LittleString = $LittleString.ToString().Trim($TrimChar)

                if ($LittleString[0] -eq '-' -and $LittleString.Length -gt 1) {
                    $LittleString = [string]"0" + $LittleString
                }
                if ($LittleString[($LittleString.Length)-1] -eq '-' -and $LittleString.Length -gt 1) {
                    $LittleString = $LittleString + [string]"10000" 
                }

                if ($LittleString.Split("-").Count -eq 2) {
                    if ( ($LittleString.Split("-").Trim()[0] -match '^[0-9]+$') -and ($LittleString.Split("-").Trim()[1] -match '^[0-9]+$')) {

                        [int]$NumStart = $LittleString.Split("-").Trim()[0]
                        [int]$NumEnd = $LittleString.Split("-").Trim()[1]
    
                        if ($NumStart -lt 0) { [int]$NumStart = 0}
                        if ($NumEnd -gt 10000) { [int]$NumEnd = 10000}

                        if ($NumStart -lt $NumEnd) {
                            $StringConc += @($NumStart..$NumEnd)
                        }
                    }
                } else {
                    $StringConc += @($LittleString -as [int])
                }
            }
            $ListOutput = [int[]]@($StringConc  | select -Unique | where {$_ -is [int] -or $_ -eq 0} | where { $_ -gt -1 -and $_ -le 10000} | sort ) #@{e={$_ -as [int]}} )
            Return $ListOutput
        } else {
            Return $null    
        }
    } else {
        Return $null
    }
}

Function NumericalListConc {
# To be used for the Display version of CollectionIDstoExclude

# Sample Input: 1,3,4,6,8,9,10,11 as Int Array
# Sample Output 1,3,4,6,8-11 as String Array
    Param ($InputString)
    $list = [int[]](NumericalListCreator $InputString)
    if ($list) {
        $ConList = @()

        for ($a=0; $a -lt @($list).count; $a++) {
            if ($a -lt @($list).count-2) {
                if ($list[$a] -eq $list[$a+1]-1 -and $list[$a] -eq $list[$a+2]-2) {
                    $b = $a+1
                    while ($b -lt @($list).count -and $list[$b] -eq $list[$b+1]-1) {
                        $b++
                    }
                    $ConList += [string]$list[$a] + "-" + $list[$b]
                    $a=$b
                } else {
                    $ConList += [string]$list[$a]
                }
            } else {
                $ConList += [string]$list[$a]
            }
        }
        return $ConList
    } else {
        return $null
    }
}

if ($MajorErrors.Count -eq 0) {
	if ((host).version.Major -le 4 -and (host).version.Major -gt 0) {
		$MajorErrors += "- Powershell v5 or newer required. Please install Windows Management Framework 5.1 or newer"	
		write-host "Powershell v5 or newer required. Please install Windows Management Framework 5.1 or newer" -ForegroundColor red
		write-host
		if ($DetailedLoggingEnabled -eq $true) {
			Add-Content -Path "$LogFile" -Value "Powershell v5 or newer required. Please install Windows Management Framework 5.1 or newer" -Encoding Unicode
		}		
	}
}


if ($MajorErrors.Count -eq 0) {
    if ($MakeChanges -eq $false) {
        $SimString = "Simulation: "
		if ($OtherSettings.Contains("- WhatIF Specified on Command Line: MakeChanges forced to False")) {
			write-host "WhatIF Specified on Command Line: MakeChanges forced to False" -ForegroundColor cyan
			$Information += "- WhatIF Specified on Command Line: MakeChanges forced to False"
		}
        write-host "Simulation mode enabled - changes will NOT be made to your users" -ForegroundColor cyan
		write-host
		$OtherSettings += "- MakeChanges: False"
        $Information += "- Simulation: No changes will be made to Students or Teachers. To change this, set MakeChanges to True in the Configuration File"
    } else {
        $SimString = ""
        write-host "This is not a simulation - changes will be made to your users" -ForegroundColor Red
        write-host
        if ( $iniFile.Misc.DontPauseForChanges -ne $true -and $iniFile.Misc.DontPauseForChanges -ne 1) {
            $PreRunPause=$true
        }
		$OtherSettings += "- MakeChanges: True"
    }

    if ($PreRunPause -eq $true) {
        if ($MakeChanges -eq $false) {
            write-host "You have 20 seconds to close this window if you want to exit STEPER now" -ForegroundColor Red
        } else {
            write-host "You have 20 seconds to close this window if you want to exit STEPER now and avoid changes" -ForegroundColor Red
        }
		Start-Sleep -s 20
		write-host
    }

    write-host "$screenstring" -foregroundcolor green
	write-host


    Set-RegistryValue -Path $regpath -Name 'StartTime' -ValueType String -Value $starttime
    Set-RegistryValue -Path $regpath -Name 'CurrentlyRunning' -ValueType DWORD -Value 1
    Set-RegistryValue -Path $regpath -Name 'LogFile' -ValueType String -Value $LogFile
    Set-RegistryValue -Path $regpath -Name 'SettingsFile' -ValueType String -Value $SettingsFileName
	Remove-ItemProperty -Path $regpath -Name 'ProcessingTime' -Force -Erroraction SilentlyContinue
	Remove-ItemProperty -Path $regpath -Name 'CompletionTime' -Force -Erroraction SilentlyContinue
    Remove-ItemProperty -Path $regpath -Name 'MajorErrors' -Force -Erroraction SilentlyContinue
    Remove-ItemProperty -Path $regpath -Name 'MajorErrorCount' -Force -Erroraction SilentlyContinue
    Remove-ItemProperty -Path $regpath -Name 'Warnings' -Force -Erroraction SilentlyContinue
    Remove-ItemProperty -Path $regpath -Name 'WarningCount' -Force -Erroraction SilentlyContinue
    Remove-ItemProperty -Path $regpath -Name 'Actions' -Force -Erroraction SilentlyContinue
    Remove-ItemProperty -Path $regpath -Name 'ActionCount' -Force -Erroraction SilentlyContinue
    Remove-ItemProperty -Path $regpath -Name 'Information' -Force -Erroraction SilentlyContinue
    Remove-ItemProperty -Path $regpath -Name 'InformationCount' -Force -Erroraction SilentlyContinue
    Remove-ItemProperty -Path $regpath -Name 'Results' -Force -Erroraction SilentlyContinue
    Remove-ItemProperty -Path $regpath -Name 'ResultsCount' -Force -Erroraction SilentlyContinue
    Remove-ItemProperty -Path $regpath -Name 'ResultsNonZeroCount' -Force -Erroraction SilentlyContinue
    Remove-ItemProperty -Path $regpath -Name 'ProcessingTimeinSeconds' -Force -Erroraction SilentlyContinue
    Remove-ItemProperty -Path $regpath -Name 'UnboundArguments' -Force -Erroraction SilentlyContinue
    Remove-ItemProperty -Path $regpath -Name 'BoundArguments' -Force -Erroraction SilentlyContinue
	Remove-ItemProperty -Path $regpath -Name 'ApplytoAllonNextRunSummary' -Force -Erroraction SilentlyContinue
	Remove-ItemProperty -Path $regpath -Name 'ApplytoAllonNextRunSummaryCount' -Force -Erroraction SilentlyContinue
	
	Remove-ItemProperty -Path $regpath -Name 'OtherSettings' -Force -Erroraction SilentlyContinue
	Remove-ItemProperty -Path $regpath -Name 'OtherSettingsCount' -Force -Erroraction SilentlyContinue
    
	Set-RegistryValue -Path $regpath -Name 'BoundArguments' -ValueType MultiString -Value $PSBoundParametersCR
    Set-RegistryValue -Path $regpath -Name 'UnboundArguments' -ValueType MultiString -Value $UnBoundParametersCR

    Set-RegistryValue -Path "$regpath\Program Information" -Name 'Version' -ValueType String -Value $Version
    Set-RegistryValue -Path "$regpath\Program Information" -Name 'Author' -ValueType String -Value $Author
    Set-RegistryValue -Path "$regpath\Program Information" -Name 'ProgramLastUpdatedDate' -ValueType String -Value $Date
    Set-RegistryValue -Path "$regpath\Program Information" -Name 'Caption' -ValueType String -Value $Caption
    Set-RegistryValue -Path "$regpath\Program Information" -Name 'Name' -ValueType String -Value $ProgramName
    Set-RegistryValue -Path "$regpath\Program Information" -Name 'AppLogName' -ValueType String -Value $AppLogName
    Set-RegistryValue -Path "$regpath\Program Information" -Name 'AppLogBaseIndex' -ValueType String -Value $AppLogBaseIndex

    $Actions += "- Processing settings"
    write-host "Processing settings" -foregroundcolor green
    $ProcessingSettings=$true
	
	
	if ($iniFile.Misc.DisableDetailedLogFile -eq $true -or $iniFile.Misc.DisableDetailedLogFile -eq 1) {
        $OtherSettings += "- Disable Detailed LogFile: True"
    } else {
        $OtherSettings += "- Disable Detailed LogFile: False"
        $OtherSettings += "- Detailed Log File: $LogFile"
        if ($DetailedLoggingEnabled -eq $true) {
            $OtherSettings += "- Log File Operating: Yes"
        } else {
            $OtherSettings += "- Log File Operating: No"
        }
    }
	$OtherSettings += "- Application Event Log Name: $AppLogName   Category: $AppLogCategory   Base Index: $AppLogBaseIndex"
	
	
	if ($iniFile.Credentials.Protect_Password -ne $false -and $iniFile.Credentials.Protect_Password -ne 0) {
		$OtherSettings += "- Automatically Protect the Global Administrator Password: True"	
	} else {
		$OtherSettings += "- Automatically Protect the Global Administrator Password: False"
	}

	if ($iniFile.Credentials.GA_Password) {
		if ($iniFile.Credentials.Protect_Password -ne $false -and $iniFile.Credentials.Protect_Password -ne 0) {
			
			$Data = $null
			$Data = Get-RegistryValue -Path "$regalt\Global Information" -Name 'Data' -ValueType String
			if ($Data.Length -ne 32) { $Data = $null }
			if ($Data -eq $null) {
				# sleep for random time in case two instances started at the same time
				[int]$time=[math]::round($pid/4,0)
				start-sleep -Milliseconds $time
				$Data = Get-RegistryValue -Path "$regalt\Global Information" -Name 'Data' -ValueType String
				if ($Data.Length -ne 32) { $Data = $null }
				if ($Data -eq $null) {
					if ((Generate-Data -Path "$regalt\Global Information") -eq $false) {
						write-host "Could not create Master Key for GA_Password Protection" -foregroundcolor red
						$MajorErrors += "- Could not create Master Key for GA_Password Protection"
					} else {
						$Actions += "- A new encryption Master Key has been created"	
					}
				} # else master key now exists
			} else {
				$Actions += "- Using existing encryption Master Key"	
			}
			$Data = Get-RegistryValue -Path "$regalt\Global Information" -Name 'Data' -ValueType String
			if ($Data.Length -ne 32) {
				Remove-ItemProperty -Path "$regalt\Global Information" -Name 'Data' -Force -Erroraction SilentlyContinue
				$Data = $null
				}
			if ($Data -ne $null) {
				$NewPassword = $null
				$NewPassword = Encode-GAP -Password $iniFile.Credentials.GA_Password -Key $Data
				if ($NewPassword -ne $null) {
					$iniFile.Credentials.ProtectedGA_Password = $NewPassword # this has just been overwritten
					if ((Update-SettingsPassword -SettingsFile $SettingsFileName -Password $NewPassword) -eq $true) {
						write-host "The GA_Password has been encrypted to protect it" -foregroundcolor yellow
						$OtherSettings += "- Protected Global Administrator Password in use: True"
						$Actions += "- The GA_Password has been encrypted to protect it"
						$inifilecontents = Get-Content $SettingsFileName
					} else {
						write-host "Could not update the Settings File with the encrypted GA_Password" -foregroundcolor red
						$MajorErrors += "- Could not update the Settings File with the encrypted GA_Password"
					}
				} else {
					$OtherSettings += "- Protected Global Administrator Password in use: False"
					Remove-ItemProperty -Path "$regalt\Global Information" -Name 'Data' -Force -Erroraction SilentlyContinue
					write-host "Could not encrypt the GA_Password. The Master Key has now been removed. Run STEPER again" -foregroundcolor red
					$MajorErrors += "- Could not encrypt the GA_Password. The Master Key has now been removed. Run STEPER again"
				}
			} else {
				$MajorErrors += "- Could not encrypt the GA_Password without a Master Key"
				write-host "Could not encrypt the GA_Password without a Master Key" -foregroundcolor red
			}
		}
		
	} else {
		if ($iniFile.Credentials.ProtectedGA_Password) {
			$Data = Get-RegistryValue -Path "$regalt\Global Information" -Name 'Data' -ValueType String
			if ($Data.Length -ne 32) {
				Remove-ItemProperty -Path "$regalt\Global Information" -Name 'Data' -Force -Erroraction SilentlyContinue
				$Data = $null
				}
			if ($Data -eq $null) {
				# protected password exists but key is missing
				write-host "Unable to decrypt the ProtectedGA_Password as the Master Key is missing or damaged. Put the GA_Password setting back into the settings file" -foregroundcolor red
				$MajorErrors += "- Unable to decrypt the ProtectedGA_Password as the Master Key is missing or damaged. Put the GA_Password setting back into the settings file"
			} else {
				$DecodedPassword = Decode-GAP -ProtectedPassword $iniFile.Credentials.ProtectedGA_Password -Key $Data
				if ($DecodedPassword -ne $null) {
					$OtherSettings += "- Protected Global Administrator Password in use: True"
					$iniFile.Credentials.GA_Password = $DecodedPassword
					$Actions += "- The ProtectedGA_Password has been decrypted for use"
				} else {
					$OtherSettings += "- Protected Global Administrator Password in use: False"
					write-host "Unable to decrypt the ProtectedGA_Password. Put the GA_Password setting back into the settings file, and if necessary delete the Master Key" -foregroundcolor red
					$MajorErrors += "- Unable to decrypt the ProtectedGA_Password. Put the GA_Password setting back into the settings file and if necessary delete the Master Key"
				}
			}
		} else {
			$OtherSettings += "- Protected Global Administrator Password in use: False"	
		}
	}
	$Data=$null
}


if ($MajorErrors.Count -eq 0) {
	
	if ($iniFile.Misc.DisableDetailedLogFile -ne $true -and $iniFile.Misc.DisableDetailedLogFile -ne 1) {
		if ($iniFile.Misc.UseCSVforUserDataInLogFile -eq $true -or $iniFile.Misc.UseCSVforUserDataInLogFile -eq 1) {
			$UseCSVforUserDataInLogFile = $true
		}		
		$OtherSettings += "- UseCSVforUserDataInLogFile: $UseCSVforUserDataInLogFile"
    }
	
	
	if ($iniFile.Collections.IgnoreCollectionIDUserType -eq $true -or $iniFile.Collections.IgnoreCollectionIDUserType -eq 1) {
		$IgnoreCollectionIDUserType = $true
	}
	$OtherSettings += "- IgnoreCollectionIDUserType in CollectionID Processing: $IgnoreCollectionIDUserType"

	
    if ($iniFile.Misc.LongJobUpdateInterval) {
        [int]$LongJobUpdateInterval=$iniFile.Misc.LongJobUpdateInterval -as [int]
        if ($LongJobUpdateInterval -eq 0) { $LongJobUpdateInterval = 100 }
        if ($LongJobUpdateInterval -lt 10) { $LongJobUpdateInterval = 10 } # 1.0.21
    }
	
	if (((Get-RegistryValue -Path $regpath -Name 'ApplytoAllTeachersOnNextRun' -ValueType DWORD ) -eq 1) -or ((Get-RegistryValue -Path $regpath -Name 'ApplytoAllStudentsOnNextRun' -ValueType DWORD ) -eq 1)) {
		write-host "User Problem Resolution via User Re-Processing has been Requested from a previous run of STEPER" -foregroundcolor yellow
	}
	
	if ($ApplytoAllTeachers -eq $true) {
		write-host "Override: Apply to All Teachers: $ApplytoAllTeachers" -foregroundcolor yellow
	}
	if ($ApplytoAllStudents -eq $true) {
		write-host "Override: Apply to All Students: $ApplytoAllStudents" -foregroundcolor yellow
	}

	#Process Major Errors with the Information - i.e. Stuff we really need

	$GAUserNameValid = $true

	$string = $null
	$string = $iniFile.Credentials.GA_Username

	if ($string) {
		if ($string -match '@' -and $string[$string.Length-1] -ne '@') {
			$Credentials += "Username: $string"
		} else {
			$GAUserNameValid = $false
			write-host "GA_Username does not appear to be a valid username" -foregroundcolor red
			$MajorErrors += "- GA_Username does not appear to be a valid username"
		}
	} else {
		$GAUserNameValid = $false
		write-host "Missing GA_Username from the Settings File" -foregroundcolor red
		$MajorErrors += "- Missing GA_Username from the Settings File"
	}

	$string = $null
	$string = $iniFile.Credentials.GA_Password

	if ($string) {
		$Credentials += "Password: [hidden]"
	} else {
		write-host "Missing GA_Password from the Settings File" -foregroundcolor red
		$MajorErrors += "- Missing GA_Password from the Settings File"
	}
	# no error if not present

	$string = $null
	$string = $iniFile.Tenancy.OnMicrosoftCOMPrefix

	if ($string) {
		$Tenancy += "Tenancy OnMicrosoftCOMPrefix: $string"
	} else {
		$Tenancy += "Tenancy OnMicrosoftCOMPrefix: (not supplied)"
	}


	# Process other command line options

    # check to see if monthly module updates disabled
    	
    if ( !($iniFile.PowerShell.DisableMonthlyModuleUpdateChecks -eq $true -or $iniFile.PowerShell.DisableMonthlyModuleUpdateChecks -eq 1)) {
        if ($NewMonth -eq $true) {
            $monthlyupdatecheck = $true
        }
    }

    if ($iniFile.PowerShell.ForceUpdateModules -eq $true -or $iniFile.PowerShell.ForceUpdateModules -eq 1 -or $UpdateModules -eq $true) {
        $monthlyupdatecheck = $true
    }
    

    # Process GlobalAdminTeamCreation
	if ( $iniFile.GlobalAdminTeamsCreation.AddGlobalAdminToCreateTeamsGroup -eq $true -or $iniFile.GlobalAdminTeamsCreation.AddGlobalAdminToCreateTeamsGroup -eq 1) {
        if ( !( $iniFile.GlobalAdminTeamsCreation.CreateTeamsGroupName) ) {
    		$MajorErrors += "- Check the Global Admin Team Creation Unified Group Name to make sure it is configured"
		} else {
		    $Tenancy += "Global Admin Team Creation Unified Group Name: $($iniFile.GlobalAdminTeamsCreation.CreateTeamsGroupName)"
		}
    }



	# Process Department
	if ($iniFile.Department.SetStudentDepartment -eq $true -or $iniFile.Department.SetStudentDepartment -eq 1) {
		$Department += "Set Student Department Based on Grade: True"
         if ($iniFile.Department.SetStudentDepartmentToAllStudents -eq $true -or $iniFile.Department.SetStudentDepartmentToAllStudents -eq 1) {
            $Department += "Set Student Department To All Students: True"
         } else {
            $Department += "Set Student Department To All Students: False"
         }
         if ($iniFile.Department.SetStudentDepartmentOnlyIfDepartmentBlank -eq $true -or $iniFile.Department.SetStudentDepartmentOnlyIfDepartmentBlank -eq 1) {
            $Department += "Set Student Department Only If Department Blank: True"
         } else {
            $Department += "Set Student Department Only If Department Blank: False"
         }
         if ($iniFile.Department.OnlyApplytoUnlicensedStudents -eq $true -or $iniFile.Department.OnlyApplytoUnlicensedStudents -eq 1) {
            $Department += "Set Student Department Only to Unlicensed Students: True"
         } else {
            $Department += "Set Student Department Only to Unlicensed Students: False"
         }
	} else {
		$Department += "Set Student Department Based on Grade: False"
	}



    # Process Licensing Groups
	if ( $iniFile.LicensingGroups.AssignTeacherstoLicensingGroup -eq $true -or $iniFile.LicensingGroups.AssignTeacherstoLicensingGroup -eq 1) {
        if ( !( $iniFile.LicensingGroups.AllTeachersLicensingGroupName) ) {
    		$MajorErrors += "- Check the Teachers Licensing settings to make sure the Licensing Group Name is configured"
		} else {
		    $LicensingInformation += "Teachers Licensing Group Name: $($iniFile.LicensingGroups.AllTeachersLicensingGroupName)"
            if ($iniFile.LicensingGroups.OnlyApplytoUnlicensedTeachers -eq $true -or $iniFile.LicensingGroups.OnlyApplytoUnlicensedTeachers -eq 1) {
                $LicensingInformation += "Add Only Unlicensed Teachers to the Licensing Group: True"
            } else {
                $LicensingInformation += "Add Only Unlicensed Teachers to the Licensing Group: False"
            }
			
			if ($iniFile.LicensingGroups.CreateAllTeachersLicensingGroupIfNotExists -eq $true -or $iniFile.LicensingGroups.CreateAllTeachersLicensingGroupIfNotExists -eq 1) {
                $LicensingInformation += "Create All Teachers Licensing Group If It Doesn't Exist: True"
            } else {
                $LicensingInformation += "Create All Teachers Licensing Group If It Doesn't Exist: False"
            }
		}
    }
	
	
	if ( $iniFile.LicensingGroups.AssignStudentstoLicensingGroup -eq $true -or $iniFile.LicensingGroups.AssignStudentstoLicensingGroup -eq 1) {
        if ( !( $iniFile.LicensingGroups.AllStudentsLicensingGroupName) ) {
    		$MajorErrors += "- Check the Students Licensing settings to make sure the Licensing Group Name is configured"
		} else {
		    $LicensingInformation += "Students Licensing Group Name: $($iniFile.LicensingGroups.AllStudentsLicensingGroupName)"
            if ($iniFile.LicensingGroups.OnlyApplytoUnlicensedStudents -eq $true -or $iniFile.LicensingGroups.OnlyApplytoUnlicensedStudents -eq 1) {
                $LicensingInformation += "Add Only Unlicensed Students to the Licensing Group: True"
            } else {
                $LicensingInformation += "Add Only Unlicensed Students to the Licensing Group: False"
            }
			
			if ($iniFile.LicensingGroups.CreateAllStudentsLicensingGroupIfNotExists -eq $true -or $iniFile.LicensingGroups.CreateAllStudentsLicensingGroupIfNotExists -eq 1) {
                $LicensingInformation += "Create All Students Licensing Group If It Doesn't Exist: True"
            } else {
                $LicensingInformation += "Create All Students Licensing Group If It Doesn't Exist: False"
            }
		}
    }



    # Process All Teachers Unified Group
	if ( $iniFile.AllTeachersGroup.AddAllTeacherstoUnifiedGroup -eq $true -or $iniFile.AllTeachersGroup.AddAllTeacherstoUnifiedGroup -eq 1) {
        if ( !( $iniFile.AllTeachersGroup.AllTeachersUnifiedGroup) ) {
    		$MajorErrors += "- Check the All Teachers Group settings to make sure the group name is configured"
		} else {
		    $UnifiedGroups += "All Teachers Unified Group Name: $($iniFile.AllTeachersGroup.AllTeachersUnifiedGroup)"
            if ($iniFile.AllTeachersGroup.OnlyApplytoUnlicensedTeachers -eq $true -or $iniFile.AllTeachersGroup.OnlyApplytoUnlicensedTeachers -eq 1) {
                $UnifiedGroups += "Add Only Unlicensed Teachers to the All Teachers Unified Group: True"
            } else {
                $UnifiedGroups += "Add Only Unlicensed Teachers to the All Teachers Unified Group: False"
            }
		}
    }


    # Process All Students Unified Group
	if ( $iniFile.AllStudentsGroup.AddAllStudentstoUnifiedGroup -eq $true -or $iniFile.AllStudentsGroup.AddAllStudentstoUnifiedGroup -eq 1) {
        if ( !( $iniFile.AllStudentsGroup.AllStudentsUnifiedGroup) ) {
    		$MajorErrors += "- Check the All Students Group settings to make sure the group name is configured"
		} else {
		    $UnifiedGroups += "All Students Unified Group Name: $($iniFile.AllStudentsGroup.AllStudentsUnifiedGroup)"
            if ($iniFile.AllStudentsGroup.OnlyApplytoUnlicensedStudents -eq $true -or $iniFile.AllStudentsGroup.OnlyApplytoUnlicensedStudents -eq 1) {
                $UnifiedGroups += "Add Only Unlicensed Students to the All Students Unified Group: True"
            } else {
                $UnifiedGroups += "Add Only Unlicensed Students to the All Students Unified Group: False"
            }
		}
    }


	# Process Email Settings
	if ($iniFile.ResultsEmail.DisableEmail -eq $true -or $iniFile.ResultsEmail.DisableEmail -eq 1) {
		$EmailAvailable = $false
	} else {
		$EmailAvailable = $true

	#Process From Address
		$string = $null
		$string = $iniFile.ResultsEmail.FromAddress

		$EmailFromAddress = $null

		if ($string) {
			$EmailFromAddress = $string
		} else {
			if ($GAUserNameValid -eq $true) {
				$EmailFromAddress = $iniFile.Credentials.GA_Username
				$Information += "- Email From Address Missing, using $($iniFile.Credentials.GA_Username)"
			}
		}

		if (!( $EmailFromAddress)) {
			$EmailAvailable = $false
			$warnings += "- Email From Address Missing; Email Disabled"
		}

	#Process To Address
		$string = $null
		$string = $iniFile.ResultsEmail.ToAddress

		$EmailToAddress = $null

		if ($string) {
			$EmailToAddress = $string
		} else {
			if ($GAUserNameValid -eq $true) {
				$EmailToAddress = $iniFile.Credentials.GA_Username
				$Information += "- Email To Address Missing, using $($iniFile.Credentials.GA_Username)"
			}
		}

		if (!( $EmailToAddress)) {
			$EmailAvailable = $false
			$warnings += "- Email To Address Missing; Email Disabled"
        }
		
	#Process Server
		$string = $null
		$string = $iniFile.ResultsEmail.Server

		$EmailServer = $null

		if ($string) {
			$EmailServer = $string
		} else {
			$EmailServer = 'smtp.office365.com'
			$warnings += "- Email To Server Missing, using $EmailServer "
		}
		
	#Process Port
		$string = $null
		$string = $iniFile.ResultsEmail.Port

		[int]$EmailPort = 0

		if ($string) {
			[int]$EmailPort = $string -as [int]
            if ($EmailPort -eq 0) {
                [int]$EmailPort = 587
			    $warnings += "- Email Port Not Configured Correctly - assuming $EmailPort"
            }
		} else {
			[int]$EmailPort = 587
			$warnings += "- Email Port missing - assuming $EmailPort"
		}

    # Process SSL
    	$EmailSSL = $true
        if ($iniFile.ResultsEmail.UseSSL -eq $false -or $iniFile.ResultsEmail.UseSSL -eq 0) {
            $EmailSSL = $false
        }

	}


	if ($EmailAvailable -eq $true) {
		$EmailInfo += "Email Available: True"
	} else {
		$EmailInfo += "Email Available: False"
	}

	$EmailInfo += "From Address: $EmailFromAddress"
	$EmailInfo += "To Address: $EmailToAddress"
	$EmailInfo += "Port: $EmailPort"
	$EmailInfo += "Server: $EmailServer"
	$EmailInfo += "UseSSL: $EmailSSL"

	if ($iniFile.ResultsEmail.OnlySendEmailonWarningorError -ne $true -and $iniFile.ResultsEmail.OnlySendEmailonWarningorError -ne 1) {
		$EmailInfo += "Only Send Email on Warning or Error: False"
	} else {
		$EmailInfo += "Only Send Email on Warning or Error: True"
	}


# Function Data Table - Map Functions to Connection Requirements

# Start - Make Connection to MSOnline and Get TeacherList

$NeedsTeacherList = $false
$NeedsStudentList = $false

$NeedsTeacherLicensingList = $false
$NeedsStudentLicensingList = $false

$NeedsAzureADPreview = $false
$NeedsMSONline = $false
$NeedsMicrosoftTeams = $false
$NeedsConnecttoExchangeOnline = $false

# Function UG-T
if ( $iniFile.AllTeachersGroup.AddAllTeacherstoUnifiedGroup -eq $true -or $iniFile.AllTeachersGroup.AddAllTeacherstoUnifiedGroup -eq 1) {
    $NeedsTeacherList = $true
    $NeedsMSONline = $true
    $NeedsConnecttoExchangeOnline = $true
}

# Function UG-S
if ( $iniFile.AllStudentsGroup.AddAllStudentstoUnifiedGroup -eq $true -or $iniFile.AllStudentsGroup.AddAllStudentstoUnifiedGroup -eq 1) {
    $NeedsStudentList = $true
    $NeedsMSONline = $true
    $NeedsConnecttoExchangeOnline = $true
}

# Function LG-T
if ( $iniFile.LicensingGroups.AssignTeacherstoLicensingGroup -eq $true -or $iniFile.LicensingGroups.AssignTeacherstoLicensingGroup -eq 1) {
    $NeedsTeacherList = $true
    $NeedsMSONline = $true
}

# Function LG-S
if ( $iniFile.LicensingGroups.AssignStudentstoLicensingGroup -eq $true -or $iniFile.LicensingGroups.AssignStudentstoLicensingGroup -eq 1) {
    $NeedsStudentList = $true
    $NeedsMSONline = $true
}
# Function DG-S
if ($iniFile.Department.SetStudentDepartment -eq $true -or $iniFile.Department.SetStudentDepartment -eq 1) {
    
    if ($iniFile.Department.SetStudentDepartmentToAllStudents -eq $true -or $iniFile.Department.SetStudentDepartmentToAllStudents -eq 1 -or $ApplytoAllStudents -eq $true) {     
        $NeedsStudentList = $true
    } else {
        $NeedsStudentLicensingList = $true
    }
    $NeedsAzureADPreview = $true
}

if ( $iniFile.GlobalAdminTeamsCreation.AddGlobalAdminToCreateTeamsGroup -eq $true -or $iniFile.GlobalAdminTeamsCreation.AddGlobalAdminToCreateTeamsGroup -eq 1)  {
    $NeedsMSONline = $true
}


# Information Retrieval - Get Unique Departments
if ($iniFile.InformationRetrieval.GetUniqueUserDepartmentNames -eq $true -or $iniFile.InformationRetrieval.GetUniqueUserDepartmentNames -eq 1) {
	$NeedsAzureADPreview = $true
	#if ($iniFile.SourceGroups.TeacherDomainFilter) { # 1.0.97
	#	$NeedsTeacherList = $true
	#}
	#if ($iniFile.SourceGroups.StudentDomainFilter) { 1.0.97
	#	$NeedsStudentList = $true
	#}
}

$FilteredSecurityGroupsToProcess = @()
[int]$ExpectedSecurityGroupActions=0


if ($iniFile.Misc.RepeatLogEventThreshold) {
    [int]$RepeatLogEventThreshold = $iniFile.Misc.RepeatLogEventThreshold -as [int]
    if ($RepeatLogEventThreshold -lt  1 -or $RepeatLogEventThreshold -gt 10000) {
        $RepeatLogEventThreshold = 20
    }
} else {
    $RepeatLogEventThreshold = 10
}
$OtherSettings += "- Repeat Event Log Suppression Threshold: $RepeatLogEventThreshold"


if ($iniFile.Misc.MaxSettingsGroups) {
    [int]$MaxSettingsGroups = $iniFile.Misc.MaxSettingsGroups -as [int]
    if ($MaxSettingsGroups -lt  10 -or $MaxSettingsGroups -gt 1000) {
        $MaxSettingsGroups = 100
    }
}
$OtherSettings += "- Maximum Settings Groups: $MaxSettingsGroups"

# Alternative Local Filtering Method
if ($iniFile.Misc.UseLocalAttributeFiltering -eq $true -or $iniFile.Misc.UseLocalAttributeFiltering -eq 1) {
	$UseLocalAttributeFiltering = $true
	[string]$FilterType = "Local"
} else {
	$UseLocalAttributeFiltering = $false
	[string]$FilterType = "Remote"
}

# Use AzureAD for Local Filtering Method
if ($iniFile.Misc.UseAzureADforLocalAttributeFilteringByDefault -eq $true -or $iniFile.Misc.UseAzureADforLocalAttributeFilteringByDefault -eq 1) {
	$UseAzureADforLocalAttributeFilteringByDefault = $true
} else {
	$UseAzureADforLocalAttributeFilteringByDefault = $false
}


if ($UseAzureADforLocalAttributeFilteringByDefault -eq $true -and $UseAzureADforLocalAttributeFilteringByDefault -eq $true) {
	write-host "Using $FilterType AzureAD Attribute Filtering by Default" -foregroundcolor green
	$OtherSettings += "- Using $FilterType AzureAD Attribute Filtering by Default"
} else {
	write-host "Using $FilterType CSOnline Attribute Filtering by Default" -foregroundcolor green
	$OtherSettings += "- Using $FilterType CSOnline Attribute Filtering by Default"
}

if ($iniFile.FunctionControl.TurnOffTeacherFilteredSecurityGroupFunctionality -ne $true -and $iniFile.FunctionControl.TurnOffTeacherFilteredSecurityGroupFunctionality -ne 1) {
	for ($a=0; $a -le $MaxSettingsGroups; $a++) {

		$String="TeacherSecurityGroup"+[string]$a
		#write-host "$string"
		# Filtered Security Groups - Teacher
		if ($iniFile.($String).EnableSecurityGroup -eq $true -or $iniFile.($String).EnableSecurityGroup -eq 1) {
			$ExpectedSecurityGroupActions +=1
			if ( !($iniFile.($String).SecurityGroupName)) {
				$MajorErrors += "- Check the [$String] Group settings. SecurityGroupName must be supplied"
			} else {
				$NeedsTeacherList = $true
				$FilteredSecurityGroupsToProcess += [PSCustomObject]@{
					'Settings' = "[TeacherSecurityGroup"+[string]$a+"]"
					'Default' = if ($iniFile.($String).CollectionID -eq 10000) { $true } else { switch($iniFile.($String).Default){"TRUE" {$true}; 1 {$true}; default {$false}} }
					'CollectionID' = switch($iniFile.($String).Default){"TRUE" {10000}; 1 {10000}; default { if ($iniFile.($String).CollectionID -ne $null) { if($iniFile.($String).CollectionID -as [int] -gt -1 -and $iniFile.($String).CollectionID -as [int] -le 10000) { $iniFile.($String).CollectionID -as [int] } else { -1 } } else { 100 }}}
					'CollectionIDIgnoreThreshold' = switch($iniFile.($String).Default){"TRUE" {0}; 1 {0}; default { if ($iniFile.($String).CollectionIDIgnoreThreshold -ne $null) { if($iniFile.($String).CollectionIDIgnoreThreshold -as [int] -gt -1 -and $iniFile.($String).CollectionIDIgnoreThreshold -as [int] -le 10000) { $iniFile.($String).CollectionIDIgnoreThreshold -as [int] } else { -1 } } else { 0 }}}
					'CollectionIDsToExclude' = switch($iniFile.($String).Default){"TRUE" { $null }; 1 { $null }; default {if ($iniFile.($String).CollectionIDsToExclude -ne $null) { [int[]](NumericalListCreator $iniFile.($String).CollectionIDsToExclude) } else { $null }}}
					'CollectionIDsToExclude_Display' = switch($iniFile.($String).Default){"TRUE" { $null }; 1 { $null }; default {if ($iniFile.($String).CollectionIDsToExclude -ne $null) { @(NumericalListConc $iniFile.($String).CollectionIDsToExclude) } else { $null }}}
					'Description' = if ($iniFile.($String).SecurityGroupDescription -ne $null) { $iniFile.($String).SecurityGroupDescription } else { "-" }
					'GroupName' = $iniFile.($String).SecurityGroupName
					'GroupNameAsID' = Test-IsGuid -ObjectGuid ($iniFile.($String).SecurityGroupName)
					'ApplyToAll' = switch($iniFile.($String).ApplyAllTeachersToGroup){"TRUE" {$true}; 1 {$true}; default {$false}}
					'ActionFilter' = $iniFile.($String).Filter
					'FilterType' = if ($UseLocalAttributeFiltering -eq $true) { "Local" } else { "Remote" }
					'FilterSource' = if ($UseLocalAttributeFiltering -eq $true) { if ($iniFile.($String).FilterSource) { switch($iniFile.($String).FilterSource.Trim($TrimChar)){ "Azure" { "AzureAD" }; "AzureAD" { "AzureAD" }; "AD" { "AzureAD" }; "CS" { "CSOnline" }; "CSO" { "CSOnline" }; "CSOnline" { "CSOnline" }; default { if ($UseAzureADforLocalAttributeFilteringByDefault -eq $true) { "AzureAD" } else { "CSOnline" } }} } else { if ($UseAzureADforLocalAttributeFilteringByDefault -eq $true) { "AzureAD" } else { "CSOnline" } } } else { "CSOnline" }
					'FilterIsOK' = if ($iniFile.($String).Filter) { Test-Filter -Codeblock $iniFile.($String).Filter -Local $UseLocalAttributeFiltering } else { $null }
					'OnlyApplyToUnlicensed' = switch($iniFile.($String).OnlyApplytoUnlicensedTeachers){"TRUE" {$true}; 1 {$true}; default {$false}}
					'RemoveUnMatchedMembers' = (@(if ($iniFile.($String).RemoveUnMatchedMembers -ne $null) { if ($iniFile.($String).RemoveUnMatchedMembers.Trim($TrimChar) -ne $null -and $iniFile.($String).RemoveUnMatchedMembers.Trim($TrimChar) -ne "") { @(iex "echo $($iniFile.($String).RemoveUnMatchedMembers.Trim($TrimChar))") | select -Unique | % { switch($_){0 {[int]-99}; "False" {[int]-99}; 2 {[int]2}; "A" {[int]2}; "Attribute" {[int]2}; "Attrib" {[int]2}; "Attributes" {[int]2}; "Att" {[int]2}; 4 {[int]4}; "G" {[int]4}; "Group" {[int]4}; "Groups" {[int]4}; "GroupRules" {[int]4}; 8 {[int]8}; "O" {[int]8}; "AD" {[int]8}; "OU" {[int]8}; "CN" {[int]8}; "DN" {[int]8}; 16 {[int]16}; "C" {[int]16}; "Collection" {[int]16}; "CollectionID" {[int]16}; 1 {[int]30}; 30 {[int]30}; 6 {[int]2,[int]4}; 10 {[int]2,[int]8}; 18 {[int]2,[int]16}; 12 {[int]4,[int]8}; 20 {[int]4,[int]16}; 24 {[int]8,[int]16}; "All" {[int]30}; "True" {[int]30}; default {[int]0}} } }  else { [int]0 } } else { [int]0 } ) | select -Unique | select -first 10 | Measure-Object -sum).sum | % {if ($_ -gt 30) { 30 } else { if ($_ -lt 0) { 0 } else { $_ } }}					
					'OnlyRemoveSameType' = switch($iniFile.($String).OnlyRemoveUnMatchedTeachers){"FALSE" {$false}; 0 {$false}; default {$true}}
					'DisableNameOrIDChecking' = switch($iniFile.($String).DisableNameOrIDChecking){"TRUE" {$true}; 1 {$true}; default {$false}}
					'OnFailureApplytoAllUsersOnNextRun' = (@(if ($iniFile.($String).OnFailureApplytoAllUsersOnNextRun -ne $null) { if ($iniFile.($String).OnFailureApplytoAllUsersOnNextRun.Trim($TrimChar) -ne $null -and $iniFile.($String).OnFailureApplytoAllUsersOnNextRun.Trim($TrimChar) -ne "") { @(iex "echo $($iniFile.($String).OnFailureApplytoAllUsersOnNextRun.Trim($TrimChar))") | select -Unique | % { switch($_){0 {[int]-99}; "False" {[int]-99}; 1 {[int]14};2 {[int]2}; "A" {[int]2}; "Assignment" {[int]2}; "AssignmentError" {[int]2}; "AssignmentErrors" {[int]2}; "Error" {[int]2}; "Errors" {[int]2}; 4 {[int]4}; "G" {[int]4}; "GroupProblem" {[int]4}; "GroupProblems" {[int]4}; 14 {[int]14}; 6 {[int]2,[int]4}; 10 {[int]2,[int]8}; 12 {[int]4,[int]8}; "All" {[int]14}; "True" {[int]14}; default {[int]0}} } }  else { [int]0 } } else { [int]0 } ) | select -Unique | select -first 10 | Measure-Object -sum).sum | % {if ($_ -gt 14) { 14 } else { if ($_ -lt 0) { 0 } else { $_ } }}
					'HasGroupRules' = if ($iniFile.($String).RequiredGroups -or $iniFile.($String).RequiredRecursiveGroups -or $iniFile.($String).RequiredOrGroups -or $iniFile.($String).RequiredOrRecursiveGroups -or $iniFile.($String).NotRequiredGroups -or $iniFile.($String).NotRequiredRecursiveGroups) { $true } else { $false }
					'RequiredGroups' = if ($iniFile.($String).RequiredGroups -ne $null) { if ($iniFile.($String).RequiredGroups -match ",") {@(iex "echo $($iniFile.($String).RequiredGroups.Trim($TrimChar))" | select -Unique ) } else { @($iniFile.($String).RequiredGroups -replace '"', "") } } else { $null }
					'RequiredRecursiveGroups' = if ($iniFile.($String).RequiredRecursiveGroups -ne $null) { if ($iniFile.($String).RequiredRecursiveGroups -match ",") { @(iex "echo $($iniFile.($String).RequiredRecursiveGroups.Trim($TrimChar))" | select -Unique ) } else { @($iniFile.($String).RequiredRecursiveGroups -replace '"', "") } } else { $null }
					'RequiredOrGroups' = if ($iniFile.($String).RequiredOrGroups -ne $null) { if ($iniFile.($String).RequiredOrGroups -match ",") {@(iex "echo $($iniFile.($String).RequiredOrGroups.Trim($TrimChar))" | select -Unique ) } else { @($iniFile.($String).RequiredOrGroups -replace '"', "") } } else { $null }
					'RequiredOrRecursiveGroups' = if ($iniFile.($String).RequiredOrRecursiveGroups -ne $null) { if ($iniFile.($String).RequiredOrRecursiveGroups -match ",") { @(iex "echo $($iniFile.($String).RequiredOrRecursiveGroups.Trim($TrimChar))" | select -Unique ) } else { @($iniFile.($String).RequiredOrRecursiveGroups -replace '"', "") } } else { $null }
					'NotRequiredGroups' = if ($iniFile.($String).NotRequiredGroups -ne $null) { if ($iniFile.($String).NotRequiredGroups -match ",") { @(iex "echo $($iniFile.($String).NotRequiredGroups.Trim($TrimChar))" | select -Unique ) } else { @($iniFile.($String).NotRequiredGroups) } } else { $null }
					'NotRequiredRecursiveGroups' = if ($iniFile.($String).NotRequiredRecursiveGroups -ne $null) { if ($iniFile.($String).NotRequiredRecursiveGroups -match ",") { @(iex "echo $($iniFile.($String).NotRequiredRecursiveGroups.Trim($TrimChar))" | select -Unique ) } else { @($iniFile.($String).NotRequiredRecursiveGroups -replace '"', "") } } else { $null }
					'DisableNameorIDCheckingForGroupRules' = switch($iniFile.($String).DisableNameorIDCheckingForGroupRules){"TRUE" {$true}; 1 {$true}; default {$false}}
					'DontAllowMissingNotRequiredGroups' = switch($iniFile.($String).DontAllowMissingNotRequiredGroups){"TRUE" {$true}; 1 {$true}; default {$false}}
					'IncludeDistributionGroups' = switch($iniFile.($String).IncludeDistributionGroupsInGroupRules){"TRUE" {$true}; 1 {$true}; default {$true}}
					'DomainFilter' = if ($iniFile.($String).DomainFilter -ne $null) { if ($iniFile.($String).DomainFilter -match ",") {@(iex "echo $($iniFile.($String).DomainFilter.Trim($TrimChar))" | select -Unique ) } else { @($iniFile.($String).DomainFilter -replace '"', "") } } else { $null }
					'HasADOURules' = if ($iniFile.($String).RequiredOrOUs -or $iniFile.($String).NotRequiredOUs ) { $true } else { $false }
					'RequiredOrOUs' = if ($iniFile.($String).RequiredOrOUs -ne $null) { if ($iniFile.($String).RequiredOrOUs -match ",") { @(iex "echo $($iniFile.($String).RequiredOrOUs.Trim($TrimChar))" | select -Unique | Sort ) } else { @($iniFile.($String).RequiredOrOUs -replace '"', "") } } else { if ($iniFile.($String).NotRequiredOUs -ne $null) { "*" } else { $null } }
					'NotRequiredOUs' = if ($iniFile.($String).NotRequiredOUs -ne $null) { if ($iniFile.($String).NotRequiredOUs -match ",") { @(iex "echo $($iniFile.($String).NotRequiredOUs.Trim($TrimChar))" | select -Unique | Sort ) } else { @($iniFile.($String).NotRequiredOUs -replace '"', "") } } else { $null }
					'SettingsNumber' = [int]$a
					'UserType' = 'Teacher'
				}
			}
		}
	}
} else {
	$Information +=	"Teacher Filtered Security Group Processing Disabled by Functionality Control"
	write-host "Teacher Filtered Security Group Processing Disabled by Functionality Control" -foregroundcolor white
}

if ($iniFile.FunctionControl.TurnOffStudentFilteredSecurityGroupFunctionality -ne $true -and $iniFile.FunctionControl.TurnOffStudentFilteredSecurityGroupFunctionality -ne 1) {
	for ($a=0; $a -le $MaxSettingsGroups; $a++) {

		$String="StudentSecurityGroup"+[string]$a
		
		# Filtered Security Groups
		if ($iniFile.($String).EnableSecurityGroup -eq $true -or $iniFile.($String).EnableSecurityGroup -eq 1) {
			$ExpectedSecurityGroupActions +=1
			if ( !($iniFile.($String).SecurityGroupName)) {
				$MajorErrors += "- Check the [$String] Group settings. SecurityGroupName must be supplied"
			} else {
				$NeedsStudentList = $true
				$FilteredSecurityGroupsToProcess += [PSCustomObject]@{
					'Settings' = "[StudentSecurityGroup"+[string]$a+"]"
					'Default' = if ($iniFile.($String).CollectionID -eq 10000) { $true } else { switch($iniFile.($String).Default){"TRUE" {$true}; 1 {$true}; default {$false}} }
					'CollectionID' = switch($iniFile.($String).Default){"TRUE" {10000}; 1 {10000}; default { if ($iniFile.($String).CollectionID -ne $null) { if($iniFile.($String).CollectionID -as [int] -gt -1 -and $iniFile.($String).CollectionID -as [int] -le 10000) { $iniFile.($String).CollectionID -as [int] } else { -1 } } else { 100 }}}
					'CollectionIDIgnoreThreshold' = switch($iniFile.($String).Default){"TRUE" {0}; 1 {0}; default { if ($iniFile.($String).CollectionIDIgnoreThreshold -ne $null) { if($iniFile.($String).CollectionIDIgnoreThreshold -as [int] -gt -1 -and $iniFile.($String).CollectionIDIgnoreThreshold -as [int] -le 10000) { $iniFile.($String).CollectionIDIgnoreThreshold -as [int] } else { -1 } } else { 0 }}}
					'CollectionIDsToExclude' = switch($iniFile.($String).Default){"TRUE" { $null }; 1 { $null }; default {if ($iniFile.($String).CollectionIDsToExclude -ne $null) { [int[]](NumericalListCreator $iniFile.($String).CollectionIDsToExclude) } else { $null }}}
					'CollectionIDsToExclude_Display' = switch($iniFile.($String).Default){"TRUE" { $null }; 1 { $null }; default {if ($iniFile.($String).CollectionIDsToExclude -ne $null) { @(NumericalListConc $iniFile.($String).CollectionIDsToExclude) } else { $null }}}
					'Description' = if ($iniFile.($String).SecurityGroupDescription -ne $null) { $iniFile.($String).SecurityGroupDescription } else { "-" }
					'GroupName' = $iniFile.($String).SecurityGroupName
					'GroupNameAsID' = Test-IsGuid -ObjectGuid ($iniFile.($String).SecurityGroupName)
					'ApplyToAll' = switch($iniFile.($String).ApplyAllStudentsToGroup){"TRUE" {$true}; 1 {$true}; default {$false}}
					'ActionFilter' = $iniFile.($String).Filter
					'FilterType' = if ($UseLocalAttributeFiltering -eq $true) { "Local" } else { "Remote" }
					'FilterSource' = if ($UseLocalAttributeFiltering -eq $true) { if ($iniFile.($String).FilterSource) { switch($iniFile.($String).FilterSource.Trim($TrimChar)){ "Azure" { "AzureAD" }; "AzureAD" { "AzureAD" }; "AD" { "AzureAD" }; "CS" { "CSOnline" }; "CSO" { "CSOnline" }; "CSOnline" { "CSOnline" }; default { if ($UseAzureADforLocalAttributeFilteringByDefault -eq $true) { "AzureAD" } else { "CSOnline" } }} } else { if ($UseAzureADforLocalAttributeFilteringByDefault -eq $true) { "AzureAD" } else { "CSOnline" } } } else { "CSOnline" }
					'FilterIsOK' = if ($iniFile.($String).Filter) { Test-Filter -CodeBlock $iniFile.($String).Filter -Local $UseLocalAttributeFiltering } else { $null }
					'OnlyApplyToUnlicensed' = switch($iniFile.($String).OnlyApplytoUnlicensedStudents){"TRUE" {$true}; 1 {$true}; default {$false}}
					'RemoveUnMatchedMembers' = (@(if ($iniFile.($String).RemoveUnMatchedMembers -ne $null) { if ($iniFile.($String).RemoveUnMatchedMembers.Trim($TrimChar) -ne $null -and $iniFile.($String).RemoveUnMatchedMembers.Trim($TrimChar) -ne "") { @(iex "echo $($iniFile.($String).RemoveUnMatchedMembers.Trim($TrimChar))") | select -Unique | % { switch($_){0 {[int]-99}; "False" {[int]-99}; 2 {[int]2}; "A" {[int]2}; "Attribute" {[int]2}; "Attrib" {[int]2}; "Attributes" {[int]2}; "Att" {[int]2}; 4 {[int]4}; "G" {[int]4}; "Group" {[int]4}; "Groups" {[int]4}; "GroupRules" {[int]4}; 8 {[int]8}; "O" {[int]8}; "AD" {[int]8}; "OU" {[int]8}; "CN" {[int]8}; "DN" {[int]8}; 16 {[int]16}; "C" {[int]16}; "Collection" {[int]16}; "CollectionID" {[int]16}; 1 {[int]30}; 30 {[int]30}; 6 {[int]2,[int]4}; 10 {[int]2,[int]8}; 18 {[int]2,[int]16}; 12 {[int]4,[int]8}; 20 {[int]4,[int]16}; 24 {[int]8,[int]16}; "All" {[int]30}; "True" {[int]30}; default {[int]0}} } }  else { [int]0 } } else { [int]0 } ) | select -Unique | select -first 10 | Measure-Object -sum).sum | % {if ($_ -gt 30) { 30 } else { if ($_ -lt 0) { 0 } else { $_ } }}					
					'OnlyRemoveSameType' = switch($iniFile.($String).OnlyRemoveUnMatchedStudents){"FALSE" {$false}; 0 {$false}; default {$true}}
					'DisableNameOrIDChecking' = switch($iniFile.($String).DisableNameOrIDChecking){"TRUE" {$true}; 1 {$true}; default {$false}}
					'OnFailureApplytoAllUsersOnNextRun' = (@(if ($iniFile.($String).OnFailureApplytoAllUsersOnNextRun -ne $null) { if ($iniFile.($String).OnFailureApplytoAllUsersOnNextRun.Trim($TrimChar) -ne $null -and $iniFile.($String).OnFailureApplytoAllUsersOnNextRun.Trim($TrimChar) -ne "") { @(iex "echo $($iniFile.($String).OnFailureApplytoAllUsersOnNextRun.Trim($TrimChar))") | select -Unique | % { switch($_){0 {[int]-99}; "False" {[int]-99}; 1 {[int]14};2 {[int]2}; "A" {[int]2}; "Assignment" {[int]2}; "AssignmentError" {[int]2}; "AssignmentErrors" {[int]2}; "Error" {[int]2}; "Errors" {[int]2}; 4 {[int]4}; "G" {[int]4}; "GroupProblem" {[int]4}; "GroupProblems" {[int]4}; 14 {[int]14}; 6 {[int]2,[int]4}; 10 {[int]2,[int]8}; 12 {[int]4,[int]8}; "All" {[int]14}; "True" {[int]14}; default {[int]0}} } }  else { [int]0 } } else { [int]0 } ) | select -Unique | select -first 10 | Measure-Object -sum).sum | % {if ($_ -gt 14) { 14 } else { if ($_ -lt 0) { 0 } else { $_ } }}
					'HasGroupRules' = if ($iniFile.($String).RequiredGroups -or $iniFile.($String).RequiredRecursiveGroups -or $iniFile.($String).RequiredOrGroups -or $iniFile.($String).RequiredOrRecursiveGroups -or $iniFile.($String).NotRequiredGroups -or $iniFile.($String).NotRequiredRecursiveGroups) { $true } else { $false }
					'RequiredGroups' = if ($iniFile.($String).RequiredGroups -ne $null) { if ($iniFile.($String).RequiredGroups -match ",") {@(iex "echo $($iniFile.($String).RequiredGroups.Trim($TrimChar))" | select -Unique ) } else { @($iniFile.($String).RequiredGroups -replace '"', "") } } else { $null }
					'RequiredRecursiveGroups' = if ($iniFile.($String).RequiredRecursiveGroups -ne $null) { if ($iniFile.($String).RequiredRecursiveGroups -match ",") { @(iex "echo $($iniFile.($String).RequiredRecursiveGroups.Trim($TrimChar))" | select -Unique ) } else { @($iniFile.($String).RequiredRecursiveGroups -replace '"', "") } } else { $null }
					'RequiredOrGroups' = if ($iniFile.($String).RequiredOrGroups -ne $null) { if ($iniFile.($String).RequiredOrGroups -match ",") {@(iex "echo $($iniFile.($String).RequiredOrGroups.Trim($TrimChar))" | select -Unique ) } else { @($iniFile.($String).RequiredOrGroups -replace '"', "") } } else { $null }
					'RequiredOrRecursiveGroups' = if ($iniFile.($String).RequiredOrRecursiveGroups -ne $null) { if ($iniFile.($String).RequiredOrRecursiveGroups -match ",") { @(iex "echo $($iniFile.($String).RequiredOrRecursiveGroups.Trim($TrimChar))" | select -Unique ) } else { @($iniFile.($String).RequiredOrRecursiveGroups -replace '"', "") } } else { $null }
					'NotRequiredGroups' = if ($iniFile.($String).NotRequiredGroups -ne $null) { if ($iniFile.($String).NotRequiredGroups -match ",") { @(iex "echo $($iniFile.($String).NotRequiredGroups.Trim($TrimChar))" | select -Unique ) } else { @($iniFile.($String).NotRequiredGroups) } } else { $null }
					'NotRequiredRecursiveGroups' = if ($iniFile.($String).NotRequiredRecursiveGroups -ne $null) { if ($iniFile.($String).NotRequiredRecursiveGroups -match ",") { @(iex "echo $($iniFile.($String).NotRequiredRecursiveGroups.Trim($TrimChar))" | select -Unique ) } else { @($iniFile.($String).NotRequiredRecursiveGroups -replace '"', "") } } else { $null }
					'DisableNameorIDCheckingForGroupRules' = switch($iniFile.($String).DisableNameorIDCheckingForGroupRules){"TRUE" {$true}; 1 {$true}; default {$false}}
					'DontAllowMissingNotRequiredGroups' = switch($iniFile.($String).DontAllowMissingNotRequiredGroups){"TRUE" {$true}; 1 {$true}; default {$false}}
					'IncludeDistributionGroups' = switch($iniFile.($String).IncludeDistributionGroupsInGroupRules){"TRUE" {$true}; 1 {$true}; default {$true}}
					'DomainFilter' = if ($iniFile.($String).DomainFilter -ne $null) { if ($iniFile.($String).DomainFilter -match ",") {@(iex "echo $($iniFile.($String).DomainFilter.Trim($TrimChar))" | select -Unique ) } else { @($iniFile.($String).DomainFilter -replace '"', "") } } else { $null }
					'HasADOURules' = if ($iniFile.($String).RequiredOrOUs -or $iniFile.($String).NotRequiredOUs ) { $true } else { $false }
					'RequiredOrOUs' = if ($iniFile.($String).RequiredOrOUs -ne $null) { if ($iniFile.($String).RequiredOrOUs -match ",") { @(iex "echo $($iniFile.($String).RequiredOrOUs.Trim($TrimChar))" | select -Unique | Sort ) } else { @($iniFile.($String).RequiredOrOUs -replace '"', "") } } else { if ($iniFile.($String).NotRequiredOUs -ne $null) { "*" } else { $null } }
					'NotRequiredOUs' = if ($iniFile.($String).NotRequiredOUs -ne $null) { if ($iniFile.($String).NotRequiredOUs -match ",") { @(iex "echo $($iniFile.($String).NotRequiredOUs.Trim($TrimChar))" | select -Unique | Sort ) } else { @($iniFile.($String).NotRequiredOUs -replace '"', "") } } else { $null }
					'SettingsNumber' = [int]$a
					'UserType' = 'Student'
				}
			}
		}
	}
} else {
	$Information +=	"Student Filtered Security Group Processing Disabled by Functionality Control"
	write-host "Student Filtered Security Group Processing Disabled by Functionality Control" -foregroundcolor white
}

$FilteredSecurityGroupsToProcess = @($FilteredSecurityGroupsToProcess | Sort @{Expression = "UserType"; Descending = $true},@{Expression = "CollectionID"; Descending = $false},@{Expression = "SettingsNumber"; Descending = $false})

$FilteredUnifiedGroupsToProcess = @()
[int]$ExpectedUnifiedGroupActions=0

if ($iniFile.FunctionControl.TurnOffTeacherFilteredUnifiedGroupFunctionality -ne $true -and $iniFile.FunctionControl.TurnOffTeacherFilteredUnifiedGroupFunctionality -ne 1) {
	for ($a=0; $a -le $MaxSettingsGroups; $a++) {

		$String="TeacherUnifiedGroup"+[string]$a
		# Filtered Unified Groups - Teacher
		if ($iniFile.($String).EnableUnifiedGroup -eq $true -or $iniFile.($String).EnableUnifiedGroup -eq 1) {
			$ExpectedUnifiedGroupActions +=1
			if ( !($iniFile.($String).UnifiedGroupName)) {
				$MajorErrors += "- Check the [$String] Unified Group settings. UnifiedGroupName must be supplied"
			} else {
				$NeedsTeacherList = $true
				$FilteredUnifiedGroupsToProcess += [PSCustomObject]@{
					'Settings' = "[TeacherUnifiedGroup"+[string]$a+"]"
					'Default' = if ($iniFile.($String).CollectionID -eq 10000) { $true } else { switch($iniFile.($String).Default){"TRUE" {$true}; 1 {$true}; default {$false}} }
					'CollectionID' = switch($iniFile.($String).Default){"TRUE" {10000}; 1 {10000}; default { if ($iniFile.($String).CollectionID -ne $null) { if($iniFile.($String).CollectionID -as [int] -gt -1 -and $iniFile.($String).CollectionID -as [int] -le 10000) { $iniFile.($String).CollectionID -as [int] } else { -1 } } else { 100 }}}
					'CollectionIDIgnoreThreshold' = switch($iniFile.($String).Default){"TRUE" {0}; 1 {0}; default { if ($iniFile.($String).CollectionIDIgnoreThreshold -ne $null) { if($iniFile.($String).CollectionIDIgnoreThreshold -as [int] -gt -1 -and $iniFile.($String).CollectionIDIgnoreThreshold -as [int] -le 10000) { $iniFile.($String).CollectionIDIgnoreThreshold -as [int] } else { -1 } } else { 0 }}}
					'CollectionIDsToExclude' = switch($iniFile.($String).Default){"TRUE" { $null }; 1 { $null }; default {if ($iniFile.($String).CollectionIDsToExclude -ne $null) { [int[]](NumericalListCreator $iniFile.($String).CollectionIDsToExclude) } else { $null }}}
					'CollectionIDsToExclude_Display' = switch($iniFile.($String).Default){"TRUE" { $null }; 1 { $null }; default {if ($iniFile.($String).CollectionIDsToExclude -ne $null) { @(NumericalListConc $iniFile.($String).CollectionIDsToExclude) } else { $null }}}
					'Description' = if ($iniFile.($String).UnifiedGroupDescription -ne $null) { $iniFile.($String).UnifiedGroupDescription } else { "-" }
					'GroupName' = $iniFile.($String).UnifiedGroupName
					'GroupNameAsID' = Test-IsGuid -ObjectGuid ($iniFile.($String).UnifiedGroupName)
					'ApplyToAll' = switch($iniFile.($String).ApplyAllTeachersToGroup){"TRUE" {$true}; 1 {$true}; default {$false}}
					'ActionFilter' = $iniFile.($String).Filter
					'FilterType' = if ($UseLocalAttributeFiltering -eq $true) { "Local" } else { "Remote" }
					'FilterSource' = if ($UseLocalAttributeFiltering -eq $true) { if ($iniFile.($String).FilterSource) { switch($iniFile.($String).FilterSource.Trim($TrimChar)){ "Azure" { "AzureAD" }; "AzureAD" { "AzureAD" }; "AD" { "AzureAD" }; "CS" { "CSOnline" }; "CSO" { "CSOnline" }; "CSOnline" { "CSOnline" }; default { if ($UseAzureADforLocalAttributeFilteringByDefault -eq $true) { "AzureAD" } else { "CSOnline" } }} } else { if ($UseAzureADforLocalAttributeFilteringByDefault -eq $true) { "AzureAD" } else { "CSOnline" } } } else { "CSOnline" }
					'FilterIsOK' = if ($iniFile.($String).Filter) { Test-Filter -Codeblock $iniFile.($String).Filter -Local $UseLocalAttributeFiltering } else { $null }
					'OnlyApplyToUnlicensed' = switch($iniFile.($String).OnlyApplytoUnlicensedTeachers){"TRUE" {$true}; 1 {$true}; default {$false}}
					'RemoveUnMatchedMembers' = (@(if ($iniFile.($String).RemoveUnMatchedMembers -ne $null) { if ($iniFile.($String).RemoveUnMatchedMembers.Trim($TrimChar) -ne $null -and $iniFile.($String).RemoveUnMatchedMembers.Trim($TrimChar) -ne "") { @(iex "echo $($iniFile.($String).RemoveUnMatchedMembers.Trim($TrimChar))") | select -Unique | % { switch($_){0 {[int]-99}; "False" {[int]-99}; 2 {[int]2}; "A" {[int]2}; "Attribute" {[int]2}; "Attrib" {[int]2}; "Attributes" {[int]2}; "Att" {[int]2}; 4 {[int]4}; "G" {[int]4}; "Group" {[int]4}; "Groups" {[int]4}; "GroupRules" {[int]4}; 8 {[int]8}; "O" {[int]8}; "AD" {[int]8}; "OU" {[int]8}; "CN" {[int]8}; "DN" {[int]8}; 16 {[int]16}; "C" {[int]16}; "Collection" {[int]16}; "CollectionID" {[int]16}; 1 {[int]30}; 30 {[int]30}; 6 {[int]2,[int]4}; 10 {[int]2,[int]8}; 18 {[int]2,[int]16}; 12 {[int]4,[int]8}; 20 {[int]4,[int]16}; 24 {[int]8,[int]16}; "All" {[int]30}; "True" {[int]30}; default {[int]0}} } }  else { [int]0 } } else { [int]0 } ) | select -Unique | select -first 10 | Measure-Object -sum).sum | % {if ($_ -gt 30) { 30 } else { if ($_ -lt 0) { 0 } else { $_ } }}					
					'OnlyRemoveSameType' = switch($iniFile.($String).OnlyRemoveUnMatchedTeachers){"FALSE" {$false}; 0 {$false}; default {$true}}
					'DisableNameOrIDChecking' = switch($iniFile.($String).DisableNameOrIDChecking){"TRUE" {$true}; 1 {$true}; default {$false}}
					'OnFailureApplytoAllUsersOnNextRun' = (@(if ($iniFile.($String).OnFailureApplytoAllUsersOnNextRun -ne $null) { if ($iniFile.($String).OnFailureApplytoAllUsersOnNextRun.Trim($TrimChar) -ne $null -and $iniFile.($String).OnFailureApplytoAllUsersOnNextRun.Trim($TrimChar) -ne "") { @(iex "echo $($iniFile.($String).OnFailureApplytoAllUsersOnNextRun.Trim($TrimChar))") | select -Unique | % { switch($_){0 {[int]-99}; "False" {[int]-99}; 1 {[int]14};2 {[int]2}; "A" {[int]2}; "Assignment" {[int]2}; "AssignmentError" {[int]2}; "AssignmentErrors" {[int]2}; "Error" {[int]2}; "Errors" {[int]2}; 4 {[int]4}; "G" {[int]4}; "GroupProblem" {[int]4}; "GroupProblems" {[int]4}; 14 {[int]14}; 6 {[int]2,[int]4}; 10 {[int]2,[int]8}; 12 {[int]4,[int]8}; "All" {[int]14}; "True" {[int]14}; default {[int]0}} } }  else { [int]0 } } else { [int]0 } ) | select -Unique | select -first 10 | Measure-Object -sum).sum | % {if ($_ -gt 14) { 14 } else { if ($_ -lt 0) { 0 } else { $_ } }}
					'HasGroupRules' = if ($iniFile.($String).RequiredGroups -or $iniFile.($String).RequiredRecursiveGroups -or $iniFile.($String).RequiredOrGroups -or $iniFile.($String).RequiredOrRecursiveGroups -or $iniFile.($String).NotRequiredGroups -or $iniFile.($String).NotRequiredRecursiveGroups) { $true } else { $false }
					'RequiredGroups' = if ($iniFile.($String).RequiredGroups -ne $null) { if ($iniFile.($String).RequiredGroups -match ",") {@(iex "echo $($iniFile.($String).RequiredGroups.Trim($TrimChar))" | select -Unique ) } else { @($iniFile.($String).RequiredGroups -replace '"', "") } } else { $null }
					'RequiredRecursiveGroups' = if ($iniFile.($String).RequiredRecursiveGroups -ne $null) { if ($iniFile.($String).RequiredRecursiveGroups -match ",") { @(iex "echo $($iniFile.($String).RequiredRecursiveGroups.Trim($TrimChar))" | select -Unique ) } else { @($iniFile.($String).RequiredRecursiveGroups -replace '"', "") } } else { $null }
					'RequiredOrGroups' = if ($iniFile.($String).RequiredOrGroups -ne $null) { if ($iniFile.($String).RequiredOrGroups -match ",") {@(iex "echo $($iniFile.($String).RequiredOrGroups.Trim($TrimChar))" | select -Unique ) } else { @($iniFile.($String).RequiredOrGroups -replace '"', "") } } else { $null }
					'RequiredOrRecursiveGroups' = if ($iniFile.($String).RequiredOrRecursiveGroups -ne $null) { if ($iniFile.($String).RequiredOrRecursiveGroups -match ",") { @(iex "echo $($iniFile.($String).RequiredOrRecursiveGroups.Trim($TrimChar))" | select -Unique ) } else { @($iniFile.($String).RequiredOrRecursiveGroups -replace '"', "") } } else { $null }
					'NotRequiredGroups' = if ($iniFile.($String).NotRequiredGroups -ne $null) { if ($iniFile.($String).NotRequiredGroups -match ",") { @(iex "echo $($iniFile.($String).NotRequiredGroups.Trim($TrimChar))" | select -Unique ) } else { @($iniFile.($String).NotRequiredGroups) } } else { $null }
					'NotRequiredRecursiveGroups' = if ($iniFile.($String).NotRequiredRecursiveGroups -ne $null) { if ($iniFile.($String).NotRequiredRecursiveGroups -match ",") { @(iex "echo $($iniFile.($String).NotRequiredRecursiveGroups.Trim($TrimChar))" | select -Unique ) } else { @($iniFile.($String).NotRequiredRecursiveGroups -replace '"', "") } } else { $null }
					'DisableNameorIDCheckingForGroupRules' = switch($iniFile.($String).DisableNameorIDCheckingForGroupRules){"TRUE" {$true}; 1 {$true}; default {$false}}
					'DontAllowMissingNotRequiredGroups' = switch($iniFile.($String).DontAllowMissingNotRequiredGroups){"TRUE" {$true}; 1 {$true}; default {$false}}
					'IncludeDistributionGroups' = switch($iniFile.($String).IncludeDistributionGroupsInGroupRules){"TRUE" {$true}; 1 {$true}; default {$true}}
					'DomainFilter' = if ($iniFile.($String).DomainFilter -ne $null) { if ($iniFile.($String).DomainFilter -match ",") {@(iex "echo $($iniFile.($String).DomainFilter.Trim($TrimChar))" | select -Unique ) } else { @($iniFile.($String).DomainFilter -replace '"', "") } } else { $null }
					'HasADOURules' = if ($iniFile.($String).RequiredOrOUs -or $iniFile.($String).NotRequiredOUs ) { $true } else { $false }
					'RequiredOrOUs' = if ($iniFile.($String).RequiredOrOUs -ne $null) { if ($iniFile.($String).RequiredOrOUs -match ",") { @(iex "echo $($iniFile.($String).RequiredOrOUs.Trim($TrimChar))" | select -Unique | Sort ) } else { @($iniFile.($String).RequiredOrOUs -replace '"', "") } } else { if ($iniFile.($String).NotRequiredOUs -ne $null) { "*" } else { $null } }
					'NotRequiredOUs' = if ($iniFile.($String).NotRequiredOUs -ne $null) { if ($iniFile.($String).NotRequiredOUs -match ",") { @(iex "echo $($iniFile.($String).NotRequiredOUs.Trim($TrimChar))" | select -Unique | Sort ) } else { @($iniFile.($String).NotRequiredOUs -replace '"', "") } } else { $null }
					'SettingsNumber' = [int]$a
					'UserType' = 'Teacher'
				}
			}
		}
	}
} else {
	$Information +=	"Teacher Filtered Unified Group Processing Disabled by Functionality Control"
	write-host "Teacher Filtered Unified Group Processing Disabled by Functionality Control" -foregroundcolor white
}

if ($iniFile.FunctionControl.TurnOffStudentFilteredUnifiedGroupFunctionality -ne $true -and $iniFile.FunctionControl.TurnOffStudentFilteredUnifiedGroupFunctionality -ne 1) {
	for ($a=0; $a -le $MaxSettingsGroups; $a++) {

		$String="StudentUnifiedGroup"+[string]$a
		# Filtered Unified Groups - Student
		#if (($iniFile.($String).EnableUnifiedGroup -eq $true -or $iniFile.($String).EnableUnifiedGroup -eq 1) -and $iniFile.($String).UnifiedGroupName) {
		if ($iniFile.($String).EnableUnifiedGroup -eq $true -or $iniFile.($String).EnableUnifiedGroup -eq 1) {
			$ExpectedUnifiedGroupActions +=1
			if ( !($iniFile.($String).UnifiedGroupName))   {
				$MajorErrors += "- Check the [$String] Unified Group settings. UnifiedGroupName must be supplied"
			} else {
				$NeedsStudentList = $true
				$FilteredUnifiedGroupsToProcess += [PSCustomObject]@{
					'Settings' = "[StudentUnifiedGroup"+[string]$a+"]"
					'Default' = if ($iniFile.($String).CollectionID -eq 10000) { $true } else { switch($iniFile.($String).Default){"TRUE" {$true}; 1 {$true}; default {$false}} }
					'CollectionID' = switch($iniFile.($String).Default){"TRUE" {10000}; 1 {10000}; default { if ($iniFile.($String).CollectionID -ne $null) { if($iniFile.($String).CollectionID -as [int] -gt -1 -and $iniFile.($String).CollectionID -as [int] -le 10000) { $iniFile.($String).CollectionID -as [int] } else { -1 } } else { 100 }}}
					'CollectionIDIgnoreThreshold' = switch($iniFile.($String).Default){"TRUE" {0}; 1 {0}; default { if ($iniFile.($String).CollectionIDIgnoreThreshold -ne $null) { if($iniFile.($String).CollectionIDIgnoreThreshold -as [int] -gt -1 -and $iniFile.($String).CollectionIDIgnoreThreshold -as [int] -le 10000) { $iniFile.($String).CollectionIDIgnoreThreshold -as [int] } else { -1 } } else { 0 }}}
					'CollectionIDsToExclude' = switch($iniFile.($String).Default){"TRUE" { $null }; 1 { $null }; default {if ($iniFile.($String).CollectionIDsToExclude -ne $null) { [int[]](NumericalListCreator $iniFile.($String).CollectionIDsToExclude) } else { $null }}}
					'CollectionIDsToExclude_Display' = switch($iniFile.($String).Default){"TRUE" { $null }; 1 { $null }; default {if ($iniFile.($String).CollectionIDsToExclude -ne $null) { @(NumericalListConc $iniFile.($String).CollectionIDsToExclude) } else { $null }}}
					'Description' = if ($iniFile.($String).UnifiedGroupDescription -ne $null) { $iniFile.($String).UnifiedGroupDescription } else { "-" }
					'GroupName' = $iniFile.($String).UnifiedGroupName
					'GroupNameAsID' = Test-IsGuid -ObjectGuid ($iniFile.($String).UnifiedGroupName)
					'ApplyToAll' = switch($iniFile.($String).ApplyAllStudentsToGroup){"TRUE" {$true}; 1 {$true}; default {$false}}
					'ActionFilter' = $iniFile.($String).Filter
					'FilterType' = if ($UseLocalAttributeFiltering -eq $true) { "Local" } else { "Remote" }
					'FilterSource' = if ($UseLocalAttributeFiltering -eq $true) { if ($iniFile.($String).FilterSource) { switch($iniFile.($String).FilterSource.Trim($TrimChar)){ "Azure" { "AzureAD" }; "AzureAD" { "AzureAD" }; "AD" { "AzureAD" }; "CS" { "CSOnline" }; "CSO" { "CSOnline" }; "CSOnline" { "CSOnline" }; default { if ($UseAzureADforLocalAttributeFilteringByDefault -eq $true) { "AzureAD" } else { "CSOnline" } }} } else { if ($UseAzureADforLocalAttributeFilteringByDefault -eq $true) { "AzureAD" } else { "CSOnline" } } } else { "CSOnline" }
					'FilterIsOK' = if ($iniFile.($String).Filter) { Test-Filter -Codeblock $iniFile.($String).Filter -Local $UseLocalAttributeFiltering } else { $null }
					'OnlyApplyToUnlicensed' = switch($iniFile.($String).OnlyApplytoUnlicensedStudents){"TRUE" {$true}; 1 {$true}; default {$false}}
					'RemoveUnMatchedMembers' = (@(if ($iniFile.($String).RemoveUnMatchedMembers -ne $null) { if ($iniFile.($String).RemoveUnMatchedMembers.Trim($TrimChar) -ne $null -and $iniFile.($String).RemoveUnMatchedMembers.Trim($TrimChar) -ne "") { @(iex "echo $($iniFile.($String).RemoveUnMatchedMembers.Trim($TrimChar))") | select -Unique | % { switch($_){0 {[int]-99}; "False" {[int]-99}; 2 {[int]2}; "A" {[int]2}; "Attribute" {[int]2}; "Attrib" {[int]2}; "Attributes" {[int]2}; "Att" {[int]2}; 4 {[int]4}; "G" {[int]4}; "Group" {[int]4}; "Groups" {[int]4}; "GroupRules" {[int]4}; 8 {[int]8}; "O" {[int]8}; "AD" {[int]8}; "OU" {[int]8}; "CN" {[int]8}; "DN" {[int]8}; 16 {[int]16}; "C" {[int]16}; "Collection" {[int]16}; "CollectionID" {[int]16}; 1 {[int]30}; 30 {[int]30}; 6 {[int]2,[int]4}; 10 {[int]2,[int]8}; 18 {[int]2,[int]16}; 12 {[int]4,[int]8}; 20 {[int]4,[int]16}; 24 {[int]8,[int]16}; "All" {[int]30}; "True" {[int]30}; default {[int]0}} } }  else { [int]0 } } else { [int]0 } ) | select -Unique | select -first 10 | Measure-Object -sum).sum | % {if ($_ -gt 30) { 30 } else { if ($_ -lt 0) { 0 } else { $_ } }}					
					'OnlyRemoveSameType' = switch($iniFile.($String).OnlyRemoveUnMatchedStudents){"FALSE" {$false}; 0 {$false}; default {$true}}
					'DisableNameOrIDChecking' = switch($iniFile.($String).DisableNameOrIDChecking){"TRUE" {$true}; 1 {$true}; default {$false}}
					'OnFailureApplytoAllUsersOnNextRun' = (@(if ($iniFile.($String).OnFailureApplytoAllUsersOnNextRun -ne $null) { if ($iniFile.($String).OnFailureApplytoAllUsersOnNextRun.Trim($TrimChar) -ne $null -and $iniFile.($String).OnFailureApplytoAllUsersOnNextRun.Trim($TrimChar) -ne "") { @(iex "echo $($iniFile.($String).OnFailureApplytoAllUsersOnNextRun.Trim($TrimChar))") | select -Unique | % { switch($_){0 {[int]-99}; "False" {[int]-99}; 1 {[int]14};2 {[int]2}; "A" {[int]2}; "Assignment" {[int]2}; "AssignmentError" {[int]2}; "AssignmentErrors" {[int]2}; "Error" {[int]2}; "Errors" {[int]2}; 4 {[int]4}; "G" {[int]4}; "GroupProblem" {[int]4}; "GroupProblems" {[int]4}; 14 {[int]14}; 6 {[int]2,[int]4}; 10 {[int]2,[int]8}; 12 {[int]4,[int]8}; "All" {[int]14}; "True" {[int]14}; default {[int]0}} } }  else { [int]0 } } else { [int]0 } ) | select -Unique | select -first 10 | Measure-Object -sum).sum | % {if ($_ -gt 14) { 14 } else { if ($_ -lt 0) { 0 } else { $_ } }}
					'HasGroupRules' = if ($iniFile.($String).RequiredGroups -or $iniFile.($String).RequiredRecursiveGroups -or $iniFile.($String).RequiredOrGroups -or $iniFile.($String).RequiredOrRecursiveGroups -or $iniFile.($String).NotRequiredGroups -or $iniFile.($String).NotRequiredRecursiveGroups) { $true } else { $false }
					'RequiredGroups' = if ($iniFile.($String).RequiredGroups -ne $null) { if ($iniFile.($String).RequiredGroups -match ",") {@(iex "echo $($iniFile.($String).RequiredGroups.Trim($TrimChar))" | select -Unique ) } else { @($iniFile.($String).RequiredGroups -replace '"', "") } } else { $null }
					'RequiredRecursiveGroups' = if ($iniFile.($String).RequiredRecursiveGroups -ne $null) { if ($iniFile.($String).RequiredRecursiveGroups -match ",") { @(iex "echo $($iniFile.($String).RequiredRecursiveGroups.Trim($TrimChar))" | select -Unique ) } else { @($iniFile.($String).RequiredRecursiveGroups -replace '"', "") } } else { $null }
					'RequiredOrGroups' = if ($iniFile.($String).RequiredOrGroups -ne $null) { if ($iniFile.($String).RequiredOrGroups -match ",") {@(iex "echo $($iniFile.($String).RequiredOrGroups.Trim($TrimChar))" | select -Unique ) } else { @($iniFile.($String).RequiredOrGroups -replace '"', "") } } else { $null }
					'RequiredOrRecursiveGroups' = if ($iniFile.($String).RequiredOrRecursiveGroups -ne $null) { if ($iniFile.($String).RequiredOrRecursiveGroups -match ",") { @(iex "echo $($iniFile.($String).RequiredOrRecursiveGroups.Trim($TrimChar))" | select -Unique ) } else { @($iniFile.($String).RequiredOrRecursiveGroups -replace '"', "") } } else { $null }
					'NotRequiredGroups' = if ($iniFile.($String).NotRequiredGroups -ne $null) { if ($iniFile.($String).NotRequiredGroups -match ",") { @(iex "echo $($iniFile.($String).NotRequiredGroups.Trim($TrimChar))" | select -Unique ) } else { @($iniFile.($String).NotRequiredGroups) } } else { $null }
					'NotRequiredRecursiveGroups' = if ($iniFile.($String).NotRequiredRecursiveGroups -ne $null) { if ($iniFile.($String).NotRequiredRecursiveGroups -match ",") { @(iex "echo $($iniFile.($String).NotRequiredRecursiveGroups.Trim($TrimChar))" | select -Unique ) } else { @($iniFile.($String).NotRequiredRecursiveGroups -replace '"', "") } } else { $null }
					'DisableNameorIDCheckingForGroupRules' = switch($iniFile.($String).DisableNameorIDCheckingForGroupRules){"TRUE" {$true}; 1 {$true}; default {$false}}
					'DontAllowMissingNotRequiredGroups' = switch($iniFile.($String).DontAllowMissingNotRequiredGroups){"TRUE" {$true}; 1 {$true}; default {$false}}
					'IncludeDistributionGroups' = switch($iniFile.($String).IncludeDistributionGroupsInGroupRules){"TRUE" {$true}; 1 {$true}; default {$true}}
					'DomainFilter' = if ($iniFile.($String).DomainFilter -ne $null) { if ($iniFile.($String).DomainFilter -match ",") {@(iex "echo $($iniFile.($String).DomainFilter.Trim($TrimChar))" | select -Unique ) } else { @($iniFile.($String).DomainFilter -replace '"', "") } } else { $null }
					'HasADOURules' = if ($iniFile.($String).RequiredOrOUs -or $iniFile.($String).NotRequiredOUs ) { $true } else { $false }
					'RequiredOrOUs' = if ($iniFile.($String).RequiredOrOUs -ne $null) { if ($iniFile.($String).RequiredOrOUs -match ",") { @(iex "echo $($iniFile.($String).RequiredOrOUs.Trim($TrimChar))" | select -Unique | Sort ) } else { @($iniFile.($String).RequiredOrOUs -replace '"', "") } } else { if ($iniFile.($String).NotRequiredOUs -ne $null) { "*" } else { $null } }
					'NotRequiredOUs' = if ($iniFile.($String).NotRequiredOUs -ne $null) { if ($iniFile.($String).NotRequiredOUs -match ",") { @(iex "echo $($iniFile.($String).NotRequiredOUs.Trim($TrimChar))" | select -Unique | Sort ) } else { @($iniFile.($String).NotRequiredOUs -replace '"', "") } } else { $null }
					'SettingsNumber' = [int]$a
					'UserType' = 'Student'
				}
			}
		}
	}
} else {
	$Information +=	"Student Filtered Unified Group Processing Disabled by Functionality Control"
	write-host "Student Filtered Unified Group Processing Disabled by Functionality Control" -foregroundcolor white
}

$FilteredUnifiedGroupsToProcess = @($FilteredUnifiedGroupsToProcess | Sort @{Expression = "UserType"; Descending = $true},@{Expression = "CollectionID"; Descending = $false},@{Expression = "SettingsNumber"; Descending = $false})


$FilteredDistributionGroupsToProcess  = @()
[int]$ExpectedDistributionGroupActions=0


if ($iniFile.FunctionControl.TurnOffTeacherFilteredDistributionGroupFunctionality -ne $true -and $iniFile.FunctionControl.TurnOffTeacherFilteredDistributionGroupFunctionality -ne 1) {
	for ($a=0; $a -le $MaxSettingsGroups; $a++) {

		$String="TeacherDistributionGroup"+[string]$a
		#write-host "$string"
		# Filtered Security Groups - Teacher
		if ($iniFile.($String).EnableDistributionGroup -eq $true -or $iniFile.($String).EnableDistributionGroup -eq 1) {
			$ExpectedDistributionGroupActions +=1
			if ( !($iniFile.($String).DistributionGroupName)) {
				$MajorErrors += "- Check the [$String] Distribution Group settings. DistributionGroupName must be supplied"
			} else {
				$NeedsTeacherList = $true
				$FilteredDistributionGroupsToProcess += [PSCustomObject]@{
					'Settings' = "[TeacherDistributionGroup"+[string]$a+"]"
					'Default' = if ($iniFile.($String).CollectionID -eq 10000) { $true } else { switch($iniFile.($String).Default){"TRUE" {$true}; 1 {$true}; default {$false}} }
					'CollectionID' = switch($iniFile.($String).Default){"TRUE" {10000}; 1 {10000}; default { if ($iniFile.($String).CollectionID -ne $null) { if($iniFile.($String).CollectionID -as [int] -gt -1 -and $iniFile.($String).CollectionID -as [int] -le 10000) { $iniFile.($String).CollectionID -as [int] } else { -1 } } else { 100 }}}
					'CollectionIDIgnoreThreshold' = switch($iniFile.($String).Default){"TRUE" {0}; 1 {0}; default { if ($iniFile.($String).CollectionIDIgnoreThreshold -ne $null) { if($iniFile.($String).CollectionIDIgnoreThreshold -as [int] -gt -1 -and $iniFile.($String).CollectionIDIgnoreThreshold -as [int] -le 10000) { $iniFile.($String).CollectionIDIgnoreThreshold -as [int] } else { -1 } } else { 0 }}}
					'CollectionIDsToExclude' = switch($iniFile.($String).Default){"TRUE" { $null }; 1 { $null }; default {if ($iniFile.($String).CollectionIDsToExclude -ne $null) { [int[]](NumericalListCreator $iniFile.($String).CollectionIDsToExclude) } else { $null }}}
					'CollectionIDsToExclude_Display' = switch($iniFile.($String).Default){"TRUE" { $null }; 1 { $null }; default {if ($iniFile.($String).CollectionIDsToExclude -ne $null) { @(NumericalListConc $iniFile.($String).CollectionIDsToExclude) } else { $null }}}
					'Description' = if ($iniFile.($String).DistributionGroupDescription -ne $null) { $iniFile.($String).DistributionGroupDescription } else { "-" }
					'GroupName' = $iniFile.($String).DistributionGroupName
					'GroupNameAsID' = Test-IsGuid -ObjectGuid ($iniFile.($String).DistributionGroupName)
					'ApplyToAll' = switch($iniFile.($String).ApplyAllTeachersToGroup){"TRUE" {$true}; 1 {$true}; default {$false}}
					'ActionFilter' = $iniFile.($String).Filter
					'FilterType' = if ($UseLocalAttributeFiltering -eq $true) { "Local" } else { "Remote" }
					'FilterSource' = if ($UseLocalAttributeFiltering -eq $true) { if ($iniFile.($String).FilterSource) { switch($iniFile.($String).FilterSource.Trim($TrimChar)){ "Azure" { "AzureAD" }; "AzureAD" { "AzureAD" }; "AD" { "AzureAD" }; "CS" { "CSOnline" }; "CSO" { "CSOnline" }; "CSOnline" { "CSOnline" }; default { if ($UseAzureADforLocalAttributeFilteringByDefault -eq $true) { "AzureAD" } else { "CSOnline" } }} } else { if ($UseAzureADforLocalAttributeFilteringByDefault -eq $true) { "AzureAD" } else { "CSOnline" } } } else { "CSOnline" }
					'FilterIsOK' = if ($iniFile.($String).Filter) { Test-Filter -Codeblock $iniFile.($String).Filter -Local $UseLocalAttributeFiltering } else { $null }
					'OnlyApplyToUnlicensed' = switch($iniFile.($String).OnlyApplytoUnlicensedTeachers){"TRUE" {$true}; 1 {$true}; default {$false}}
					'RemoveUnMatchedMembers' = (@(if ($iniFile.($String).RemoveUnMatchedMembers -ne $null) { if ($iniFile.($String).RemoveUnMatchedMembers.Trim($TrimChar) -ne $null -and $iniFile.($String).RemoveUnMatchedMembers.Trim($TrimChar) -ne "") { @(iex "echo $($iniFile.($String).RemoveUnMatchedMembers.Trim($TrimChar))") | select -Unique | % { switch($_){0 {[int]-99}; "False" {[int]-99}; 2 {[int]2}; "A" {[int]2}; "Attribute" {[int]2}; "Attrib" {[int]2}; "Attributes" {[int]2}; "Att" {[int]2}; 4 {[int]4}; "G" {[int]4}; "Group" {[int]4}; "Groups" {[int]4}; "GroupRules" {[int]4}; 8 {[int]8}; "O" {[int]8}; "AD" {[int]8}; "OU" {[int]8}; "CN" {[int]8}; "DN" {[int]8}; 16 {[int]16}; "C" {[int]16}; "Collection" {[int]16}; "CollectionID" {[int]16}; 1 {[int]30}; 30 {[int]30}; 6 {[int]2,[int]4}; 10 {[int]2,[int]8}; 18 {[int]2,[int]16}; 12 {[int]4,[int]8}; 20 {[int]4,[int]16}; 24 {[int]8,[int]16}; "All" {[int]30}; "True" {[int]30}; default {[int]0}} } }  else { [int]0 } } else { [int]0 } ) | select -Unique | select -first 10 | Measure-Object -sum).sum | % {if ($_ -gt 30) { 30 } else { if ($_ -lt 0) { 0 } else { $_ } }}					
					'OnlyRemoveSameType' = switch($iniFile.($String).OnlyRemoveUnMatchedTeachers){"FALSE" {$false}; 0 {$false}; default {$true}}
					'DisableNameOrIDChecking' = switch($iniFile.($String).DisableNameOrIDChecking){"TRUE" {$true}; 1 {$true}; default {$false}}
					'OnFailureApplytoAllUsersOnNextRun' = (@(if ($iniFile.($String).OnFailureApplytoAllUsersOnNextRun -ne $null) { if ($iniFile.($String).OnFailureApplytoAllUsersOnNextRun.Trim($TrimChar) -ne $null -and $iniFile.($String).OnFailureApplytoAllUsersOnNextRun.Trim($TrimChar) -ne "") { @(iex "echo $($iniFile.($String).OnFailureApplytoAllUsersOnNextRun.Trim($TrimChar))") | select -Unique | % { switch($_){0 {[int]-99}; "False" {[int]-99}; 1 {[int]14};2 {[int]2}; "A" {[int]2}; "Assignment" {[int]2}; "AssignmentError" {[int]2}; "AssignmentErrors" {[int]2}; "Error" {[int]2}; "Errors" {[int]2}; 4 {[int]4}; "G" {[int]4}; "GroupProblem" {[int]4}; "GroupProblems" {[int]4}; 14 {[int]14}; 6 {[int]2,[int]4}; 10 {[int]2,[int]8}; 12 {[int]4,[int]8}; "All" {[int]14}; "True" {[int]14}; default {[int]0}} } }  else { [int]0 } } else { [int]0 } ) | select -Unique | select -first 10 | Measure-Object -sum).sum | % {if ($_ -gt 14) { 14 } else { if ($_ -lt 0) { 0 } else { $_ } }}
					'HasGroupRules' = if ($iniFile.($String).RequiredGroups -or $iniFile.($String).RequiredRecursiveGroups -or $iniFile.($String).RequiredOrGroups -or $iniFile.($String).RequiredOrRecursiveGroups -or $iniFile.($String).NotRequiredGroups -or $iniFile.($String).NotRequiredRecursiveGroups) { $true } else { $false }
					'RequiredGroups' = if ($iniFile.($String).RequiredGroups -ne $null) { if ($iniFile.($String).RequiredGroups -match ",") {@(iex "echo $($iniFile.($String).RequiredGroups.Trim($TrimChar))" | select -Unique ) } else { @($iniFile.($String).RequiredGroups -replace '"', "") } } else { $null }
					'RequiredRecursiveGroups' = if ($iniFile.($String).RequiredRecursiveGroups -ne $null) { if ($iniFile.($String).RequiredRecursiveGroups -match ",") { @(iex "echo $($iniFile.($String).RequiredRecursiveGroups.Trim($TrimChar))" | select -Unique ) } else { @($iniFile.($String).RequiredRecursiveGroups -replace '"', "") } } else { $null }
					'RequiredOrGroups' = if ($iniFile.($String).RequiredOrGroups -ne $null) { if ($iniFile.($String).RequiredOrGroups -match ",") {@(iex "echo $($iniFile.($String).RequiredOrGroups.Trim($TrimChar))" | select -Unique ) } else { @($iniFile.($String).RequiredOrGroups -replace '"', "") } } else { $null }
					'RequiredOrRecursiveGroups' = if ($iniFile.($String).RequiredOrRecursiveGroups -ne $null) { if ($iniFile.($String).RequiredOrRecursiveGroups -match ",") { @(iex "echo $($iniFile.($String).RequiredOrRecursiveGroups.Trim($TrimChar))" | select -Unique ) } else { @($iniFile.($String).RequiredOrRecursiveGroups -replace '"', "") } } else { $null }
					'NotRequiredGroups' = if ($iniFile.($String).NotRequiredGroups -ne $null) { if ($iniFile.($String).NotRequiredGroups -match ",") { @(iex "echo $($iniFile.($String).NotRequiredGroups.Trim($TrimChar))" | select -Unique ) } else { @($iniFile.($String).NotRequiredGroups) } } else { $null }
					'NotRequiredRecursiveGroups' = if ($iniFile.($String).NotRequiredRecursiveGroups -ne $null) { if ($iniFile.($String).NotRequiredRecursiveGroups -match ",") { @(iex "echo $($iniFile.($String).NotRequiredRecursiveGroups.Trim($TrimChar))" | select -Unique ) } else { @($iniFile.($String).NotRequiredRecursiveGroups -replace '"', "") } } else { $null }
					'DisableNameorIDCheckingForGroupRules' = switch($iniFile.($String).DisableNameorIDCheckingForGroupRules){"TRUE" {$true}; 1 {$true}; default {$false}}
					'DontAllowMissingNotRequiredGroups' = switch($iniFile.($String).DontAllowMissingNotRequiredGroups){"TRUE" {$true}; 1 {$true}; default {$false}}
					'IncludeDistributionGroups' = switch($iniFile.($String).IncludeDistributionGroupsInGroupRules){"TRUE" {$true}; 1 {$true}; default {$true}}
					'DomainFilter' = if ($iniFile.($String).DomainFilter -ne $null) { if ($iniFile.($String).DomainFilter -match ",") {@(iex "echo $($iniFile.($String).DomainFilter.Trim($TrimChar))" | select -Unique ) } else { @($iniFile.($String).DomainFilter -replace '"', "") } } else { $null }
					'HasADOURules' = if ($iniFile.($String).RequiredOrOUs -or $iniFile.($String).NotRequiredOUs ) { $true } else { $false }
					'RequiredOrOUs' = if ($iniFile.($String).RequiredOrOUs -ne $null) { if ($iniFile.($String).RequiredOrOUs -match ",") { @(iex "echo $($iniFile.($String).RequiredOrOUs.Trim($TrimChar))" | select -Unique | Sort ) } else { @($iniFile.($String).RequiredOrOUs -replace '"', "") } } else { if ($iniFile.($String).NotRequiredOUs -ne $null) { "*" } else { $null } }
					'NotRequiredOUs' = if ($iniFile.($String).NotRequiredOUs -ne $null) { if ($iniFile.($String).NotRequiredOUs -match ",") { @(iex "echo $($iniFile.($String).NotRequiredOUs.Trim($TrimChar))" | select -Unique | Sort ) } else { @($iniFile.($String).NotRequiredOUs -replace '"', "") } } else { $null }
					'SettingsNumber' = [int]$a
					'UserType' = 'Teacher'
				}
			}
		}
	}
} else {
	$Information +=	"Teacher Filtered Distribution Group Processing Disabled by Functionality Control"
	write-host "Teacher Filtered Distribution Group Processing Disabled by Functionality Control" -foregroundcolor white
}

if ($iniFile.FunctionControl.TurnOffStudentFilteredDistributionGroupFunctionality -ne $true -and $iniFile.FunctionControl.TurnOffStudentFilteredDistributionGroupFunctionality -ne 1) {
	for ($a=0; $a -le $MaxSettingsGroups; $a++) {

		$String="StudentDistributionGroup"+[string]$a
		
		# Filtered Security Groups
		if ($iniFile.($String).EnableDistributionGroup -eq $true -or $iniFile.($String).EnableDistributionGroup -eq 1) {
			$ExpectedDistributionGroupActions +=1
			if ( !($iniFile.($String).DistributionGroupName) ) {
				$MajorErrors += "- Check the [$String] Distribution Group settings. DistributionGroupName must be supplied"
			} else {
				$NeedsStudentList = $true
				$FilteredDistributionGroupsToProcess += [PSCustomObject]@{
					'Settings' = "[StudentDistributionGroup"+[string]$a+"]"
					'Default' = if ($iniFile.($String).CollectionID -eq 10000) { $true } else { switch($iniFile.($String).Default){"TRUE" {$true}; 1 {$true}; default {$false}} }
					'CollectionID' = switch($iniFile.($String).Default){"TRUE" {10000}; 1 {10000}; default { if ($iniFile.($String).CollectionID -ne $null) { if($iniFile.($String).CollectionID -as [int] -gt -1 -and $iniFile.($String).CollectionID -as [int] -le 10000) { $iniFile.($String).CollectionID -as [int] } else { -1 } } else { 100 }}}
					'CollectionIDIgnoreThreshold' = switch($iniFile.($String).Default){"TRUE" {0}; 1 {0}; default { if ($iniFile.($String).CollectionIDIgnoreThreshold -ne $null) { if($iniFile.($String).CollectionIDIgnoreThreshold -as [int] -gt -1 -and $iniFile.($String).CollectionIDIgnoreThreshold -as [int] -le 10000) { $iniFile.($String).CollectionIDIgnoreThreshold -as [int] } else { -1 } } else { 0 }}}
					'CollectionIDsToExclude' = switch($iniFile.($String).Default){"TRUE" { $null }; 1 { $null }; default {if ($iniFile.($String).CollectionIDsToExclude -ne $null) { [int[]](NumericalListCreator $iniFile.($String).CollectionIDsToExclude) } else { $null }}}
					'CollectionIDsToExclude_Display' = switch($iniFile.($String).Default){"TRUE" { $null }; 1 { $null }; default {if ($iniFile.($String).CollectionIDsToExclude -ne $null) { @(NumericalListConc $iniFile.($String).CollectionIDsToExclude) } else { $null }}}
					'Description' = if ($iniFile.($String).DistributionGroupDescription -ne $null) { $iniFile.($String).DistributionGroupDescription } else { "-"}
					'GroupName' = $iniFile.($String).DistributionGroupName
					'GroupNameAsID' = Test-IsGuid -ObjectGuid ($iniFile.($String).DistributionGroupName)
					'ApplyToAll' = switch($iniFile.($String).ApplyAllStudentsToGroup){"TRUE" {$true}; 1 {$true}; default {$false}}
					'ActionFilter' = $iniFile.($String).Filter
					'FilterType' = if ($UseLocalAttributeFiltering -eq $true) { "Local" } else { "Remote" }
					'FilterSource' = if ($UseLocalAttributeFiltering -eq $true) { if ($iniFile.($String).FilterSource) { switch($iniFile.($String).FilterSource.Trim($TrimChar)){ "Azure" { "AzureAD" }; "AzureAD" { "AzureAD" }; "AD" { "AzureAD" }; "CS" { "CSOnline" }; "CSO" { "CSOnline" }; "CSOnline" { "CSOnline" }; default { if ($UseAzureADforLocalAttributeFilteringByDefault -eq $true) { "AzureAD" } else { "CSOnline" } }} } else { if ($UseAzureADforLocalAttributeFilteringByDefault -eq $true) { "AzureAD" } else { "CSOnline" } } } else { "CSOnline" }
					'FilterIsOK' = if ($iniFile.($String).Filter) { Test-Filter -Codeblock $iniFile.($String).Filter -Local $UseLocalAttributeFiltering } else { $null }
					'OnlyApplyToUnlicensed' = switch($iniFile.($String).OnlyApplytoUnlicensedStudents){"TRUE" {$true}; 1 {$true}; default {$false}}
					'RemoveUnMatchedMembers' = (@(if ($iniFile.($String).RemoveUnMatchedMembers -ne $null) { if ($iniFile.($String).RemoveUnMatchedMembers.Trim($TrimChar) -ne $null -and $iniFile.($String).RemoveUnMatchedMembers.Trim($TrimChar) -ne "") { @(iex "echo $($iniFile.($String).RemoveUnMatchedMembers.Trim($TrimChar))") | select -Unique | % { switch($_){0 {[int]-99}; "False" {[int]-99}; 2 {[int]2}; "A" {[int]2}; "Attribute" {[int]2}; "Attrib" {[int]2}; "Attributes" {[int]2}; "Att" {[int]2}; 4 {[int]4}; "G" {[int]4}; "Group" {[int]4}; "Groups" {[int]4}; "GroupRules" {[int]4}; 8 {[int]8}; "O" {[int]8}; "AD" {[int]8}; "OU" {[int]8}; "CN" {[int]8}; "DN" {[int]8}; 16 {[int]16}; "C" {[int]16}; "Collection" {[int]16}; "CollectionID" {[int]16}; 1 {[int]30}; 30 {[int]30}; 6 {[int]2,[int]4}; 10 {[int]2,[int]8}; 18 {[int]2,[int]16}; 12 {[int]4,[int]8}; 20 {[int]4,[int]16}; 24 {[int]8,[int]16}; "All" {[int]30}; "True" {[int]30}; default {[int]0}} } }  else { [int]0 } } else { [int]0 } ) | select -Unique | select -first 10 | Measure-Object -sum).sum | % {if ($_ -gt 30) { 30 } else { if ($_ -lt 0) { 0 } else { $_ } }}					
					'OnlyRemoveSameType' = switch($iniFile.($String).OnlyRemoveUnMatchedStudents){"FALSE" {$false}; 0 {$false}; default {$true}}
					'DisableNameOrIDChecking' = switch($iniFile.($String).DisableNameOrIDChecking){"TRUE" {$true}; 1 {$true}; default {$false}}
					'OnFailureApplytoAllUsersOnNextRun' = (@(if ($iniFile.($String).OnFailureApplytoAllUsersOnNextRun -ne $null) { if ($iniFile.($String).OnFailureApplytoAllUsersOnNextRun.Trim($TrimChar) -ne $null -and $iniFile.($String).OnFailureApplytoAllUsersOnNextRun.Trim($TrimChar) -ne "") { @(iex "echo $($iniFile.($String).OnFailureApplytoAllUsersOnNextRun.Trim($TrimChar))") | select -Unique | % { switch($_){0 {[int]-99}; "False" {[int]-99}; 1 {[int]14};2 {[int]2}; "A" {[int]2}; "Assignment" {[int]2}; "AssignmentError" {[int]2}; "AssignmentErrors" {[int]2}; "Error" {[int]2}; "Errors" {[int]2}; 4 {[int]4}; "G" {[int]4}; "GroupProblem" {[int]4}; "GroupProblems" {[int]4}; 14 {[int]14}; 6 {[int]2,[int]4}; 10 {[int]2,[int]8}; 12 {[int]4,[int]8}; "All" {[int]14}; "True" {[int]14}; default {[int]0}} } }  else { [int]0 } } else { [int]0 } ) | select -Unique | select -first 10 | Measure-Object -sum).sum | % {if ($_ -gt 14) { 14 } else { if ($_ -lt 0) { 0 } else { $_ } }}
					'HasGroupRules' = if ($iniFile.($String).RequiredGroups -or $iniFile.($String).RequiredRecursiveGroups -or $iniFile.($String).RequiredOrGroups -or $iniFile.($String).RequiredOrRecursiveGroups -or $iniFile.($String).NotRequiredGroups -or $iniFile.($String).NotRequiredRecursiveGroups) { $true } else { $false }
					'RequiredGroups' = if ($iniFile.($String).RequiredGroups -ne $null) { if ($iniFile.($String).RequiredGroups -match ",") {@(iex "echo $($iniFile.($String).RequiredGroups.Trim($TrimChar))" | select -Unique ) } else { @($iniFile.($String).RequiredGroups -replace '"', "") } } else { $null }
					'RequiredRecursiveGroups' = if ($iniFile.($String).RequiredRecursiveGroups -ne $null) { if ($iniFile.($String).RequiredRecursiveGroups -match ",") { @(iex "echo $($iniFile.($String).RequiredRecursiveGroups.Trim($TrimChar))" | select -Unique ) } else { @($iniFile.($String).RequiredRecursiveGroups -replace '"', "") } } else { $null }
					'RequiredOrGroups' = if ($iniFile.($String).RequiredOrGroups -ne $null) { if ($iniFile.($String).RequiredOrGroups -match ",") {@(iex "echo $($iniFile.($String).RequiredOrGroups.Trim($TrimChar))" | select -Unique ) } else { @($iniFile.($String).RequiredOrGroups -replace '"', "") } } else { $null }
					'RequiredOrRecursiveGroups' = if ($iniFile.($String).RequiredOrRecursiveGroups -ne $null) { if ($iniFile.($String).RequiredOrRecursiveGroups -match ",") { @(iex "echo $($iniFile.($String).RequiredOrRecursiveGroups.Trim($TrimChar))" | select -Unique ) } else { @($iniFile.($String).RequiredOrRecursiveGroups -replace '"', "") } } else { $null }
					'NotRequiredGroups' = if ($iniFile.($String).NotRequiredGroups -ne $null) { if ($iniFile.($String).NotRequiredGroups -match ",") { @(iex "echo $($iniFile.($String).NotRequiredGroups.Trim($TrimChar))" | select -Unique ) } else { @($iniFile.($String).NotRequiredGroups) } } else { $null }
					'NotRequiredRecursiveGroups' = if ($iniFile.($String).NotRequiredRecursiveGroups -ne $null) { if ($iniFile.($String).NotRequiredRecursiveGroups -match ",") { @(iex "echo $($iniFile.($String).NotRequiredRecursiveGroups.Trim($TrimChar))" | select -Unique ) } else { @($iniFile.($String).NotRequiredRecursiveGroups -replace '"', "") } } else { $null }
					'DisableNameorIDCheckingForGroupRules' = switch($iniFile.($String).DisableNameorIDCheckingForGroupRules){"TRUE" {$true}; 1 {$true}; default {$false}}
					'DontAllowMissingNotRequiredGroups' = switch($iniFile.($String).DontAllowMissingNotRequiredGroups){"TRUE" {$true}; 1 {$true}; default {$false}}
					'IncludeDistributionGroups' = switch($iniFile.($String).IncludeDistributionGroupsInGroupRules){"TRUE" {$true}; 1 {$true}; default {$true}}
					'DomainFilter' = if ($iniFile.($String).DomainFilter -ne $null) { if ($iniFile.($String).DomainFilter -match ",") {@(iex "echo $($iniFile.($String).DomainFilter.Trim($TrimChar))" | select -Unique ) } else { @($iniFile.($String).DomainFilter -replace '"', "") } } else { $null }
					'HasADOURules' = if ($iniFile.($String).RequiredOrOUs -or $iniFile.($String).NotRequiredOUs ) { $true } else { $false }
					'RequiredOrOUs' = if ($iniFile.($String).RequiredOrOUs -ne $null) { if ($iniFile.($String).RequiredOrOUs -match ",") { @(iex "echo $($iniFile.($String).RequiredOrOUs.Trim($TrimChar))" | select -Unique | Sort ) } else { @($iniFile.($String).RequiredOrOUs -replace '"', "") } } else { if ($iniFile.($String).NotRequiredOUs -ne $null) { "*" } else { $null } }
					'NotRequiredOUs' = if ($iniFile.($String).NotRequiredOUs -ne $null) { if ($iniFile.($String).NotRequiredOUs -match ",") { @(iex "echo $($iniFile.($String).NotRequiredOUs.Trim($TrimChar))" | select -Unique | Sort ) } else { @($iniFile.($String).NotRequiredOUs -replace '"', "") } } else { $null }
					'SettingsNumber' = [int]$a
					'UserType' = 'Student'
				}
			}
		}
	}
} else {
	$Information +=	"Student Filtered Distribution Group Processing Disabled by Functionality Control"
	write-host "Student Filtered Distribution Group Processing Disabled by Functionality Control" -foregroundcolor white
}

$FilteredDistributionGroupsToProcess = @($FilteredDistributionGroupsToProcess | Sort @{Expression = "UserType"; Descending = $true},@{Expression = "CollectionID"; Descending = $false},@{Expression = "SettingsNumber"; Descending = $false})



$PackagesToProcess = @()
[int]$ExpectedPackageActions=0

if ($iniFile.FunctionControl.TurnOffTeacherFilteredPackageFunctionality -ne $true -and $iniFile.FunctionControl.TurnOffTeacherFilteredPackageFunctionality -ne 1) {
	for ($a=0; $a -le $MaxSettingsGroups; $a++) {

		$warning=$false
		$String="TeacherTeamsPackages"+[string]$a
		# Teams Packages - Teacher
		if ($iniFile.($String).ApplyTeamsPackage -eq $true -or $iniFile.($String).ApplyTeamsPackage -eq 1) {
			$ExpectedPackageActions +=1
			if ( !($iniFile.($String).TeamsPackage)) {
				$MajorErrors += "- Check the [$String] Package settings. TeamsPackage must be supplied"
				$warning = $true
			}

			if ($warning -eq $false) {
				$NeedsTeacherList = $true
				$PackagesToProcess += [PSCustomObject]@{
					'Settings' = "[TeacherTeamsPackages"+[string]$a+"]"
					'Default' = if ($iniFile.($String).CollectionID -eq 10000) { $true } else { switch($iniFile.($String).Default){"TRUE" {$true}; 1 {$true}; default {$false}} }
					'CollectionID' = switch($iniFile.($String).Default){"TRUE" {10000}; 1 {10000}; default { if ($iniFile.($String).CollectionID -ne $null) { if($iniFile.($String).CollectionID -as [int] -gt -1 -and $iniFile.($String).CollectionID -as [int] -le 10000) { $iniFile.($String).CollectionID -as [int] } else { -1 } } else { 100 }}}
					'CollectionIDIgnoreThreshold' = switch($iniFile.($String).Default){"TRUE" {0}; 1 {0}; default { if ($iniFile.($String).CollectionIDIgnoreThreshold -ne $null) { if($iniFile.($String).CollectionIDIgnoreThreshold -as [int] -gt -1 -and $iniFile.($String).CollectionIDIgnoreThreshold -as [int] -le 10000) { $iniFile.($String).CollectionIDIgnoreThreshold -as [int] } else { -1 } } else { 0 }}}
					'CollectionIDsToExclude' = switch($iniFile.($String).Default){"TRUE" { $null }; 1 { $null }; default {if ($iniFile.($String).CollectionIDsToExclude -ne $null) { [int[]](NumericalListCreator $iniFile.($String).CollectionIDsToExclude) } else { $null }}}
					'CollectionIDsToExclude_Display' = switch($iniFile.($String).Default){"TRUE" { $null }; 1 { $null }; default {if ($iniFile.($String).CollectionIDsToExclude -ne $null) { @(NumericalListConc $iniFile.($String).CollectionIDsToExclude) } else { $null }}}
					'Description' = if ($iniFile.($String).TeamsPackageDescription) { $iniFile.($String).TeamsPackageDescription } else { "-" }
					'Package' = $iniFile.($String).TeamsPackage
					'ApplyOnlyIfExistingPackageBlank' = switch($iniFile.($String).OnlyChangeTeamsPackageIfBlank){"FALSE" {$false}; 0 {$false}; default {$true}}
					'ApplyToAll' = switch($iniFile.($String).ApplyTeamsPackagestoAllTeachers){"TRUE" {$true}; 1 {$true}; default {$false}}
					'ActionFilter' = $iniFile.($String).Filter
					'FilterType' = if ($UseLocalAttributeFiltering -eq $true) { "Local" } else { "Remote" }
					'FilterSource' = if ($UseLocalAttributeFiltering -eq $true) { if ($iniFile.($String).FilterSource) { switch($iniFile.($String).FilterSource.Trim($TrimChar)){ "Azure" { "AzureAD" }; "AzureAD" { "AzureAD" }; "AD" { "AzureAD" }; "CS" { "CSOnline" }; "CSO" { "CSOnline" }; "CSOnline" { "CSOnline" }; default { if ($UseAzureADforLocalAttributeFilteringByDefault -eq $true) { "AzureAD" } else { "CSOnline" } }} } else { if ($UseAzureADforLocalAttributeFilteringByDefault -eq $true) { "AzureAD" } else { "CSOnline" } } } else { "CSOnline" }
					'FilterIsOK' = if ($iniFile.($String).Filter) { Test-Filter -Codeblock $iniFile.($String).Filter -Local $UseLocalAttributeFiltering } else { $null }
					'OnlyApplyToUnlicensed' = switch($iniFile.($String).OnlyApplytoUnlicensedTeachers){"TRUE" {$true}; 1 {$true}; default {$false}}
					'SkipUpdatedLicensingCheck' = switch($iniFile.($String).SkipUpdatedLicensingCheck){"TRUE" {$true}; 1 {$true}; default {$false}}
					'ProcessDisabledUsers' = switch($iniFile.($String).ProcessDisabledUsers){"TRUE" {$true}; 1 {$true}; default {$false}}
					'RemoveFailedUsersFromLicensingGroup' = (@(if ($iniFile.($String).RemoveFailedUsersFromLicensingGroup -ne $null) { if ($iniFile.($String).RemoveFailedUsersFromLicensingGroup.Trim($TrimChar) -ne $null -and $iniFile.($String).RemoveFailedUsersFromLicensingGroup.Trim($TrimChar) -ne "") { @(iex "echo $($iniFile.($String).RemoveFailedUsersFromLicensingGroup.Trim($TrimChar))") | select -Unique | % { switch($_){0 {[int]-99}; "False" {[int]-99}; 1 {[int]14}; 2 {[int]2}; "A" {[int]2}; "Assignment" {[int]2}; "AssignmentError" {[int]2}; "AssignmentErrors" {[int]2}; "Error" {[int]2}; "Errors" {[int]2}; 4 {[int]4}; "Disabled" {[int]4}; "D" {[int]4}; "NotEnabled" {[int]4}; 8 {[int]8}; "N" {[int]8}; "NotLicensed" {[int]8}; "NoLicense" {[int]8}; 6 {[int]2,[int]4}; 10 {[int]2,[int]8}; 12 {[int]4,[int]8}; 14 {[int]14}; "All" {[int]14}; "True" {[int]14}; default {[int]0}} } }  else { [int]0 } } else { [int]0 } ) | select -Unique | select -first 10 | Measure-Object -sum).sum | % {if ($_ -gt 14) { 14 } else { if ($_ -lt 0) { 0 } else { $_ } }}
					'OnFailureApplytoAllUsersOnNextRun' = (@(if ($iniFile.($String).OnFailureApplytoAllUsersOnNextRun -ne $null) { if ($iniFile.($String).OnFailureApplytoAllUsersOnNextRun.Trim($TrimChar) -ne $null -and $iniFile.($String).OnFailureApplytoAllUsersOnNextRun.Trim($TrimChar) -ne "") { @(iex "echo $($iniFile.($String).OnFailureApplytoAllUsersOnNextRun.Trim($TrimChar))") | select -Unique | % { switch($_){0 {[int]-99}; "False" {[int]-99}; 1 {[int]14};2 {[int]2}; "A" {[int]2}; "Assignment" {[int]2}; "AssignmentError" {[int]2}; "AssignmentErrors" {[int]2}; "Error" {[int]2}; "Errors" {[int]2}; 4 {[int]4}; "Disabled" {[int]4}; "D" {[int]4}; "NotEnabled" {[int]4}; 8 {[int]8}; "N" {[int]8}; "NotLicensed" {[int]8}; "NoLicense" {[int]8}; 6 {[int]2,[int]4}; 10 {[int]2,[int]8}; 12 {[int]4,[int]8}; 14 {[int]14}; "All" {[int]14}; "True" {[int]14}; default {[int]0}} } } else { [int]0 } } else { [int]0 } ) | select -Unique | select -first 10 | Measure-Object -sum).sum | % {if ($_ -gt 14) { 14 } else { if ($_ -lt 0) { 0 } else { $_ } }}
					'HasGroupRules' = if ($iniFile.($String).RequiredGroups -or $iniFile.($String).RequiredRecursiveGroups -or $iniFile.($String).RequiredOrGroups -or $iniFile.($String).RequiredOrRecursiveGroups -or $iniFile.($String).NotRequiredGroups -or $iniFile.($String).NotRequiredRecursiveGroups) { $true } else { $false }
					'RequiredGroups' = if ($iniFile.($String).RequiredGroups -ne $null) { if ($iniFile.($String).RequiredGroups -match ",") {@(iex "echo $($iniFile.($String).RequiredGroups.Trim($TrimChar))" | select -Unique ) } else { @($iniFile.($String).RequiredGroups -replace '"', "") } } else { $null }
					'RequiredRecursiveGroups' = if ($iniFile.($String).RequiredRecursiveGroups -ne $null) { if ($iniFile.($String).RequiredRecursiveGroups -match ",") { @(iex "echo $($iniFile.($String).RequiredRecursiveGroups.Trim($TrimChar))" | select -Unique ) } else { @($iniFile.($String).RequiredRecursiveGroups -replace '"', "") } } else { $null }
					'RequiredOrGroups' = if ($iniFile.($String).RequiredOrGroups -ne $null) { if ($iniFile.($String).RequiredOrGroups -match ",") {@(iex "echo $($iniFile.($String).RequiredOrGroups.Trim($TrimChar))" | select -Unique ) } else { @($iniFile.($String).RequiredOrGroups -replace '"', "") } } else { $null }
					'RequiredOrRecursiveGroups' = if ($iniFile.($String).RequiredOrRecursiveGroups -ne $null) { if ($iniFile.($String).RequiredOrRecursiveGroups -match ",") { @(iex "echo $($iniFile.($String).RequiredOrRecursiveGroups.Trim($TrimChar))" | select -Unique ) } else { @($iniFile.($String).RequiredOrRecursiveGroups -replace '"', "") } } else { $null }
					'NotRequiredGroups' = if ($iniFile.($String).NotRequiredGroups -ne $null) { if ($iniFile.($String).NotRequiredGroups -match ",") { @(iex "echo $($iniFile.($String).NotRequiredGroups.Trim($TrimChar))" | select -Unique ) } else { @($iniFile.($String).NotRequiredGroups) } } else { $null }
					'NotRequiredRecursiveGroups' = if ($iniFile.($String).NotRequiredRecursiveGroups -ne $null) { if ($iniFile.($String).NotRequiredRecursiveGroups -match ",") { @(iex "echo $($iniFile.($String).NotRequiredRecursiveGroups.Trim($TrimChar))" | select -Unique ) } else { @($iniFile.($String).NotRequiredRecursiveGroups -replace '"', "") } } else { $null }
					'DisableNameorIDCheckingForGroupRules' = switch($iniFile.($String).DisableNameorIDCheckingForGroupRules){"TRUE" {$true}; 1 {$true}; default {$false}}
					'DontAllowMissingNotRequiredGroups' = switch($iniFile.($String).DontAllowMissingNotRequiredGroups){"TRUE" {$true}; 1 {$true}; default {$false}}
					'IncludeDistributionGroups' = switch($iniFile.($String).IncludeDistributionGroupsInGroupRules){"TRUE" {$true}; 1 {$true}; default {$true}}
					'DomainFilter' = if ($iniFile.($String).DomainFilter -ne $null) { if ($iniFile.($String).DomainFilter -match ",") {@(iex "echo $($iniFile.($String).DomainFilter.Trim($TrimChar))" | select -Unique ) } else { @($iniFile.($String).DomainFilter -replace '"', "") } } else { $null }
					'HasADOURules' = if ($iniFile.($String).RequiredOrOUs -or $iniFile.($String).NotRequiredOUs ) { $true } else { $false }
					'RequiredOrOUs' = if ($iniFile.($String).RequiredOrOUs -ne $null) { if ($iniFile.($String).RequiredOrOUs -match ",") { @(iex "echo $($iniFile.($String).RequiredOrOUs.Trim($TrimChar))" | select -Unique | Sort ) } else { @($iniFile.($String).RequiredOrOUs -replace '"', "") } } else { if ($iniFile.($String).NotRequiredOUs -ne $null) { "*" } else { $null } }
					'NotRequiredOUs' = if ($iniFile.($String).NotRequiredOUs -ne $null) { if ($iniFile.($String).NotRequiredOUs -match ",") { @(iex "echo $($iniFile.($String).NotRequiredOUs.Trim($TrimChar))" | select -Unique | Sort ) } else { @($iniFile.($String).NotRequiredOUs -replace '"', "") } } else { $null }
					'SettingsNumber' = [int]$a
					'UserType' = 'Teacher'
				}
			}
		}
	}
} else {
	$Information +=	"Teacher Filtered Package Processing Disabled by Functionality Control"
	write-host "Teacher Filtered Package Processing Disabled by Functionality Control" -foregroundcolor white
}

if ($iniFile.FunctionControl.TurnOffStudentFilteredPackageFunctionality -ne $true -and $iniFile.FunctionControl.TurnOffStudentFilteredPackageFunctionality -ne 1) {
	for ($a=0; $a -le $MaxSettingsGroups; $a++) {

		$warning=$false
		$String="StudentTeamsPackages"+[string]$a
		# Teams Packages - Student
		if ($iniFile.($String).ApplyTeamsPackage -eq $true -or $iniFile.($String).ApplyTeamsPackage -eq 1) {
			$ExpectedPackageActions +=1
			if ( !($iniFile.($String).TeamsPackage)) {
				$MajorErrors += "- Check the [$String] Package settings. TeamsPackage must be supplied"
				$warning = $true
			}

			if ($warning -eq $false) {        
				$NeedsStudentList= $true
				$PackagesToProcess += [PSCustomObject]@{
					'Settings' = "[StudentTeamsPackages"+[string]$a+"]"
					'Default' = if ($iniFile.($String).CollectionID -eq 10000) { $true } else { switch($iniFile.($String).Default){"TRUE" {$true}; 1 {$true}; default {$false}} }
					'CollectionID' = switch($iniFile.($String).Default){"TRUE" {10000}; 1 {10000}; default { if ($iniFile.($String).CollectionID -ne $null) { if($iniFile.($String).CollectionID -as [int] -gt -1 -and $iniFile.($String).CollectionID -as [int] -le 10000) { $iniFile.($String).CollectionID -as [int] } else { -1 } } else { 100 }}}
					'CollectionIDIgnoreThreshold' = switch($iniFile.($String).Default){"TRUE" {0}; 1 {0}; default { if ($iniFile.($String).CollectionIDIgnoreThreshold -ne $null) { if($iniFile.($String).CollectionIDIgnoreThreshold -as [int] -gt -1 -and $iniFile.($String).CollectionIDIgnoreThreshold -as [int] -le 10000) { $iniFile.($String).CollectionIDIgnoreThreshold -as [int] } else { -1 } } else { 0 }}}
					'CollectionIDsToExclude' = switch($iniFile.($String).Default){"TRUE" { $null }; 1 { $null }; default {if ($iniFile.($String).CollectionIDsToExclude -ne $null) { [int[]](NumericalListCreator $iniFile.($String).CollectionIDsToExclude) } else { $null }}}
					'CollectionIDsToExclude_Display' = switch($iniFile.($String).Default){"TRUE" { $null }; 1 { $null }; default {if ($iniFile.($String).CollectionIDsToExclude -ne $null) { @(NumericalListConc $iniFile.($String).CollectionIDsToExclude) } else { $null }}}
					'Description' = if ($iniFile.($String).TeamsPackageDescription) { $iniFile.($String).TeamsPackageDescription } else { "-" }
					'Package' = $iniFile.($String).TeamsPackage
					'ApplyOnlyIfExistingPackageBlank' = switch($iniFile.($String).OnlyChangeTeamsPackageIfBlank){"FALSE" {$false}; 0 {$false}; default {$true}}
					'ApplyToAll' = switch($iniFile.($String).ApplyTeamsPackagestoAllStudents){"TRUE" {$true}; 1 {$true}; default {$false}}
					'ActionFilter' = $iniFile.($String).Filter
					'FilterType' = if ($UseLocalAttributeFiltering -eq $true) { "Local" } else { "Remote" }
					'FilterSource' = if ($UseLocalAttributeFiltering -eq $true) { if ($iniFile.($String).FilterSource) { switch($iniFile.($String).FilterSource.Trim( )){ "Azure" { "AzureAD" }; "AzureAD" { "AzureAD" }; "AD" { "AzureAD" }; "CS" { "CSOnline" }; "CSO" { "CSOnline" }; "CSOnline" { "CSOnline" }; default { if ($UseAzureADforLocalAttributeFilteringByDefault -eq $true) { "AzureAD" } else { "CSOnline" } }} } else { if ($UseAzureADforLocalAttributeFilteringByDefault -eq $true) { "AzureAD" } else { "CSOnline" } } } else { "CSOnline" }
					'FilterIsOK' = if ($iniFile.($String).Filter) { Test-Filter -Codeblock $iniFile.($String).Filter -Local $UseLocalAttributeFiltering } else { $null }
					'OnlyApplyToUnlicensed' = switch($iniFile.StudentTeamsPackages1.OnlyApplytoUnlicensedStudents){"TRUE" {$true}; 1 {$true}; default {$false}}
					'SkipUpdatedLicensingCheck' = switch($iniFile.($String).SkipUpdatedLicensingCheck){"TRUE" {$true}; 1 {$true}; default {$false}}
					'ProcessDisabledUsers' = switch($iniFile.($String).ProcessDisabledUsers){"TRUE" {$true}; 1 {$true}; default {$false}}
					'RemoveFailedUsersFromLicensingGroup' = (@(if ($iniFile.($String).RemoveFailedUsersFromLicensingGroup -ne $null) { if ($iniFile.($String).RemoveFailedUsersFromLicensingGroup.Trim($TrimChar) -ne $null -and $iniFile.($String).RemoveFailedUsersFromLicensingGroup.Trim($TrimChar) -ne "") { @(iex "echo $($iniFile.($String).RemoveFailedUsersFromLicensingGroup.Trim($TrimChar))") | select -Unique | % { switch($_){0 {[int]-99}; "False" {[int]-99}; 1 {[int]14}; 2 {[int]2}; "A" {[int]2}; "Assignment" {[int]2}; "AssignmentError" {[int]2}; "AssignmentErrors" {[int]2}; "Error" {[int]2}; "Errors" {[int]2}; 4 {[int]4}; "Disabled" {[int]4}; "D" {[int]4}; "NotEnabled" {[int]4}; 8 {[int]8}; "N" {[int]8}; "NotLicensed" {[int]8}; "NoLicense" {[int]8}; 6 {[int]2,[int]4}; 10 {[int]2,[int]8}; 12 {[int]4,[int]8}; 14 {[int]14}; "All" {[int]14}; "True" {[int]14}; default {[int]0}} } }  else { [int]0 } } else { [int]0 } ) | select -Unique | select -first 10 | Measure-Object -sum).sum | % {if ($_ -gt 14) { 14 } else { if ($_ -lt 0) { 0 } else { $_ } }}
					'OnFailureApplytoAllUsersOnNextRun' = (@(if ($iniFile.($String).OnFailureApplytoAllUsersOnNextRun -ne $null) { if ($iniFile.($String).OnFailureApplytoAllUsersOnNextRun.Trim($TrimChar) -ne $null -and $iniFile.($String).OnFailureApplytoAllUsersOnNextRun.Trim($TrimChar) -ne "") { @(iex "echo $($iniFile.($String).OnFailureApplytoAllUsersOnNextRun.Trim($TrimChar))") | select -Unique | % { switch($_){0 {[int]-99}; "False" {[int]-99}; 1 {[int]14};2 {[int]2}; "A" {[int]2}; "Assignment" {[int]2}; "AssignmentError" {[int]2}; "AssignmentErrors" {[int]2}; "Error" {[int]2}; "Errors" {[int]2}; 4 {[int]4}; "Disabled" {[int]4}; "D" {[int]4}; "NotEnabled" {[int]4}; 8 {[int]8}; "N" {[int]8}; "NotLicensed" {[int]8}; "NoLicense" {[int]8}; 6 {[int]2,[int]4}; 10 {[int]2,[int]8}; 12 {[int]4,[int]8}; 14 {[int]14}; "All" {[int]14}; "True" {[int]14}; default {[int]0}} } } else { [int]0 } } else { [int]0 } ) | select -Unique | select -first 10 | Measure-Object -sum).sum | % {if ($_ -gt 14) { 14 } else { if ($_ -lt 0) { 0 } else { $_ } }}
					'HasGroupRules' = if ($iniFile.($String).RequiredGroups -or $iniFile.($String).RequiredRecursiveGroups -or $iniFile.($String).RequiredOrGroups -or $iniFile.($String).RequiredOrRecursiveGroups -or $iniFile.($String).NotRequiredGroups -or $iniFile.($String).NotRequiredRecursiveGroups) { $true } else { $false }
					'RequiredGroups' = if ($iniFile.($String).RequiredGroups -ne $null) { if ($iniFile.($String).RequiredGroups -match ",") {@(iex "echo $($iniFile.($String).RequiredGroups.Trim($TrimChar))" | select -Unique ) } else { @($iniFile.($String).RequiredGroups -replace '"', "") } } else { $null }
					'RequiredRecursiveGroups' = if ($iniFile.($String).RequiredRecursiveGroups -ne $null) { if ($iniFile.($String).RequiredRecursiveGroups -match ",") { @(iex "echo $($iniFile.($String).RequiredRecursiveGroups.Trim($TrimChar))" | select -Unique ) } else { @($iniFile.($String).RequiredRecursiveGroups -replace '"', "") } } else { $null }
					'RequiredOrGroups' = if ($iniFile.($String).RequiredOrGroups -ne $null) { if ($iniFile.($String).RequiredOrGroups -match ",") {@(iex "echo $($iniFile.($String).RequiredOrGroups.Trim($TrimChar))" | select -Unique ) } else { @($iniFile.($String).RequiredOrGroups -replace '"', "") } } else { $null }
					'RequiredOrRecursiveGroups' = if ($iniFile.($String).RequiredOrRecursiveGroups -ne $null) { if ($iniFile.($String).RequiredOrRecursiveGroups -match ",") { @(iex "echo $($iniFile.($String).RequiredOrRecursiveGroups.Trim($TrimChar))" | select -Unique ) } else { @($iniFile.($String).RequiredOrRecursiveGroups -replace '"', "") } } else { $null }
					'NotRequiredGroups' = if ($iniFile.($String).NotRequiredGroups -ne $null) { if ($iniFile.($String).NotRequiredGroups -match ",") { @(iex "echo $($iniFile.($String).NotRequiredGroups.Trim($TrimChar))" | select -Unique ) } else { @($iniFile.($String).NotRequiredGroups) } } else { $null }
					'NotRequiredRecursiveGroups' = if ($iniFile.($String).NotRequiredRecursiveGroups -ne $null) { if ($iniFile.($String).NotRequiredRecursiveGroups -match ",") { @(iex "echo $($iniFile.($String).NotRequiredRecursiveGroups.Trim($TrimChar))" | select -Unique ) } else { @($iniFile.($String).NotRequiredRecursiveGroups -replace '"', "") } } else { $null }
					'DisableNameorIDCheckingForGroupRules' = switch($iniFile.($String).DisableNameorIDCheckingForGroupRules){"TRUE" {$true}; 1 {$true}; default {$false}}
					'DontAllowMissingNotRequiredGroups' = switch($iniFile.($String).DontAllowMissingNotRequiredGroups){"TRUE" {$true}; 1 {$true}; default {$false}}
					'IncludeDistributionGroups' = switch($iniFile.($String).IncludeDistributionGroupsInGroupRules){"TRUE" {$true}; 1 {$true}; default {$true}}
					'DomainFilter' = if ($iniFile.($String).DomainFilter -ne $null) { if ($iniFile.($String).DomainFilter -match ",") {@(iex "echo $($iniFile.($String).DomainFilter.Trim($TrimChar))" | select -Unique ) } else { @($iniFile.($String).DomainFilter -replace '"', "") } } else { $null }
					'HasADOURules' = if ($iniFile.($String).RequiredOrOUs -or $iniFile.($String).NotRequiredOUs ) { $true } else { $false }
					'RequiredOrOUs' = if ($iniFile.($String).RequiredOrOUs -ne $null) { if ($iniFile.($String).RequiredOrOUs -match ",") { @(iex "echo $($iniFile.($String).RequiredOrOUs.Trim($TrimChar))" | select -Unique | Sort ) } else { @($iniFile.($String).RequiredOrOUs -replace '"', "") } } else { if ($iniFile.($String).NotRequiredOUs -ne $null) { "*" } else { $null } }
					'NotRequiredOUs' = if ($iniFile.($String).NotRequiredOUs -ne $null) { if ($iniFile.($String).NotRequiredOUs -match ",") { @(iex "echo $($iniFile.($String).NotRequiredOUs.Trim($TrimChar))" | select -Unique | Sort ) } else { @($iniFile.($String).NotRequiredOUs -replace '"', "") } } else { $null }
					'SettingsNumber' = [int]$a
					'UserType' = 'Student'
				}
			}
		}
	}
} else {
	$Information +=	"Student Filtered Package Processing Disabled by Functionality Control"
	write-host "Student Filtered Package Processing Disabled by Functionality Control" -foregroundcolor white
}

$PackagesToProcess = @($PackagesToProcess | Sort @{Expression = "UserType"; Descending = $true},@{Expression = "CollectionID"; Descending = $false},@{Expression = "SettingsNumber"; Descending = $false})

$PolicyList = @('TeamsAppPermissionPolicy','TeamsAppSetupPolicy','TeamsCallingPolicy','TeamsChannelsPolicy','TeamsFeedbackPolicy','TeamsMeetingBroadcastPolicy','TeamsMeetingPolicy','TeamsMessagingPolicy')
              

# Assignment Errors 1
# Disabled          2
# Not licensed      4
# All               8

$PoliciesToProcess = @()
[int]$ExpectedPolicyActions=0
if ($iniFile.FunctionControl.TurnOffTeacherFilteredPolicyFunctionality -ne $true -and $iniFile.FunctionControl.TurnOffTeacherFilteredPolicyFunctionality -ne 1) {
	for ($a=0; $a -le $MaxSettingsGroups; $a++) {

		$warning=$false
		$String="TeacherTeamsPolicies"+[string]$a
		# Set Policies - Teacher
		if (($iniFile.($String).ApplyTeamsPolicies -eq $true -or $iniFile.($String).ApplyTeamsPolicies -eq 1) -and !($iniFile.($String).TeamsMessagingPolicy) -and !($iniFile.($String).TeamsMeetingPolicy) -and !($iniFile.($String).TeamsAppSetupPolicy) -and !($iniFile.($String).TeamsCallingPolicy) -and !($iniFile.($String).TeamsMeetingBroadcastPolicy) -and !($iniFile.($String).TeamsChannelsPolicy) -and !($iniFile.($String).TeamsAppPermissionPolicy)  -and !($iniFile.($String).TeamsFeedbackPolicy)) {
			#$ExpectedPolicyActions +=1
			$MajorErrors += "- Check the [$String] Policies settings. No Policies names have been supplied"
			$warning = $true
		}
		
		if ($warning -eq $false) {
			foreach ($PolicyItem in $PolicyList) {
				if (($iniFile.($String).ApplyTeamsPolicies -eq $true -or $iniFile.($String).ApplyTeamsPolicies -eq 1) -and $iniFile.($String).($PolicyItem)) {
					$ExpectedPolicyActions +=1
					$NeedsTeacherList = $true
					$ApplyOnlyIfExisting="OnlyChange" + [string]$PolicyItem + "IfBlank"
					$PoliciesToProcess += [PSCustomObject]@{
						'Settings' = "[TeacherTeamsPolicies"+[string]$a+"]"
						'Default' = if ($iniFile.($String).CollectionID -eq 10000) { $true } else { switch($iniFile.($String).Default){"TRUE" {$true}; 1 {$true}; default {$false}} }
						'CollectionID' = switch($iniFile.($String).Default){"TRUE" {10000}; 1 {10000}; default { if ($iniFile.($String).CollectionID -ne $null) { if($iniFile.($String).CollectionID -as [int] -gt -1 -and $iniFile.($String).CollectionID -as [int] -le 10000) { $iniFile.($String).CollectionID -as [int] } else { -1 } } else { 100 }}}
						'CollectionIDIgnoreThreshold' = switch($iniFile.($String).Default){"TRUE" {0}; 1 {0}; default { if ($iniFile.($String).CollectionIDIgnoreThreshold -ne $null) { if($iniFile.($String).CollectionIDIgnoreThreshold -as [int] -gt -1 -and $iniFile.($String).CollectionIDIgnoreThreshold -as [int] -le 10000) { $iniFile.($String).CollectionIDIgnoreThreshold -as [int] } else { -1 } } else { 0 }}}
						'CollectionIDsToExclude' = switch($iniFile.($String).Default){"TRUE" { $null }; 1 { $null }; default {if ($iniFile.($String).CollectionIDsToExclude -ne $null) { [int[]](NumericalListCreator $iniFile.($String).CollectionIDsToExclude) } else { $null }}}
						'CollectionIDsToExclude_Display' = switch($iniFile.($String).Default){"TRUE" { $null }; 1 { $null }; default {if ($iniFile.($String).CollectionIDsToExclude -ne $null) { @(NumericalListConc $iniFile.($String).CollectionIDsToExclude) } else { $null }}}
						'Description' = if ($iniFile.($String).TeamsPoliciesDescription) { $iniFile.($String).TeamsPoliciesDescription } else { "-" }
						'ActionType' = $PolicyItem
						'ActionPolicy' = $iniFile.($String).($PolicyItem)
						'ApplyOnlyIfExistingPolicyBlank' = switch($iniFile.($String).($ApplyOnlyIfExisting)){"FALSE" {$false}; 0 {$false}; default {$true}}
						'ApplyToAll' = switch($iniFile.($String).ApplyTeamsPoliciestoAllTeachers){"TRUE" {$true}; 1 {$true}; default {$false}}
						'ActionFilter' = $iniFile.($String).Filter
						'FilterType' = if ($UseLocalAttributeFiltering -eq $true) { "Local" } else { "Remote" }
						'FilterSource' = if ($UseLocalAttributeFiltering -eq $true) { if ($iniFile.($String).FilterSource) { switch($iniFile.($String).FilterSource.Trim($TrimChar)){ "Azure" { "AzureAD" }; "AzureAD" { "AzureAD" }; "AD" { "AzureAD" }; "CS" { "CSOnline" }; "CSO" { "CSOnline" }; "CSOnline" { "CSOnline" }; default { if ($UseAzureADforLocalAttributeFilteringByDefault -eq $true) { "AzureAD" } else { "CSOnline" } }} } else { if ($UseAzureADforLocalAttributeFilteringByDefault -eq $true) { "AzureAD" } else { "CSOnline" } } } else { "CSOnline" }
						'FilterIsOK' = if ($iniFile.($String).Filter) { Test-Filter -Codeblock $iniFile.($String).Filter -Local $UseLocalAttributeFiltering } else { $null }
						'OnlyApplyToUnlicensed' = switch($iniFile.($String).OnlyApplytoUnlicensedTeachers){"TRUE" {$true}; 1 {$true}; default {$false}}
						'SkipUpdatedLicensingCheck' = switch($iniFile.($String).SkipUpdatedLicensingCheck){"TRUE" {$true}; 1 {$true}; default {$false}}
						'ProcessDisabledUsers' = switch($iniFile.($String).ProcessDisabledUsers){"TRUE" {$true}; 1 {$true}; default {$false}}
						'RemoveFailedUsersFromLicensingGroup' = (@(if ($iniFile.($String).RemoveFailedUsersFromLicensingGroup -ne $null) { if ($iniFile.($String).RemoveFailedUsersFromLicensingGroup.Trim($TrimChar) -ne $null -and $iniFile.($String).RemoveFailedUsersFromLicensingGroup.Trim($TrimChar) -ne "") { @(iex "echo $($iniFile.($String).RemoveFailedUsersFromLicensingGroup.Trim($TrimChar))") | select -Unique | % { switch($_){0 {[int]-99}; "False" {[int]-99}; 1 {[int]14}; 2 {[int]2}; "A" {[int]2}; "Assignment" {[int]2}; "AssignmentError" {[int]2}; "AssignmentErrors" {[int]2}; "Error" {[int]2}; "Errors" {[int]2}; 4 {[int]4}; "Disabled" {[int]4}; "D" {[int]4}; "NotEnabled" {[int]4}; 8 {[int]8}; "N" {[int]8}; "NotLicensed" {[int]8}; "NoLicense" {[int]8}; 6 {[int]2,[int]4}; 10 {[int]2,[int]8}; 12 {[int]4,[int]8}; 14 {[int]14}; "All" {[int]14}; "True" {[int]14}; default {[int]0}} } }  else { [int]0 } } else { [int]0 } ) | select -Unique | select -first 10 | Measure-Object -sum).sum | % {if ($_ -gt 14) { 14 } else { if ($_ -lt 0) { 0 } else { $_ } }}
						'OnFailureApplytoAllUsersOnNextRun' = (@(if ($iniFile.($String).OnFailureApplytoAllUsersOnNextRun -ne $null) { if ($iniFile.($String).OnFailureApplytoAllUsersOnNextRun.Trim($TrimChar) -ne $null -and $iniFile.($String).OnFailureApplytoAllUsersOnNextRun.Trim($TrimChar) -ne "") { @(iex "echo $($iniFile.($String).OnFailureApplytoAllUsersOnNextRun.Trim($TrimChar))") | select -Unique | % { switch($_){0 {[int]-99}; "False" {[int]-99}; 1 {[int]14};2 {[int]2}; "A" {[int]2}; "Assignment" {[int]2}; "AssignmentError" {[int]2}; "AssignmentErrors" {[int]2}; "Error" {[int]2}; "Errors" {[int]2}; 4 {[int]4}; "Disabled" {[int]4}; "D" {[int]4}; "NotEnabled" {[int]4}; 8 {[int]8}; "N" {[int]8}; "NotLicensed" {[int]8}; "NoLicense" {[int]8}; 6 {[int]2,[int]4}; 10 {[int]2,[int]8}; 12 {[int]4,[int]8}; 14 {[int]14}; "All" {[int]14}; "True" {[int]14}; default {[int]0}} } } else { [int]0 } } else { [int]0 } ) | select -Unique | select -first 10 | Measure-Object -sum).sum | % {if ($_ -gt 14) { 14 } else { if ($_ -lt 0) { 0 } else { $_ } }}
						'HasGroupRules' = if ($iniFile.($String).RequiredGroups -or $iniFile.($String).RequiredRecursiveGroups -or $iniFile.($String).RequiredOrGroups -or $iniFile.($String).RequiredOrRecursiveGroups -or $iniFile.($String).NotRequiredGroups -or $iniFile.($String).NotRequiredRecursiveGroups) { $true } else { $false }
						'RequiredGroups' = if ($iniFile.($String).RequiredGroups -ne $null) { if ($iniFile.($String).RequiredGroups -match ",") {@(iex "echo $($iniFile.($String).RequiredGroups.Trim($TrimChar))" | select -Unique ) } else { @($iniFile.($String).RequiredGroups -replace '"', "") } } else { $null }
						'RequiredRecursiveGroups' = if ($iniFile.($String).RequiredRecursiveGroups -ne $null) { if ($iniFile.($String).RequiredRecursiveGroups -match ",") { @(iex "echo $($iniFile.($String).RequiredRecursiveGroups.Trim($TrimChar))" | select -Unique ) } else { @($iniFile.($String).RequiredRecursiveGroups -replace '"', "") } } else { $null }
						'RequiredOrGroups' = if ($iniFile.($String).RequiredOrGroups -ne $null) { if ($iniFile.($String).RequiredOrGroups -match ",") {@(iex "echo $($iniFile.($String).RequiredOrGroups.Trim($TrimChar))" | select -Unique ) } else { @($iniFile.($String).RequiredOrGroups -replace '"', "") } } else { $null }
						'RequiredOrRecursiveGroups' = if ($iniFile.($String).RequiredOrRecursiveGroups -ne $null) { if ($iniFile.($String).RequiredOrRecursiveGroups -match ",") { @(iex "echo $($iniFile.($String).RequiredOrRecursiveGroups.Trim($TrimChar))" | select -Unique ) } else { @($iniFile.($String).RequiredOrRecursiveGroups -replace '"', "") } } else { $null }
						'NotRequiredGroups' = if ($iniFile.($String).NotRequiredGroups -ne $null) { if ($iniFile.($String).NotRequiredGroups -match ",") { @(iex "echo $($iniFile.($String).NotRequiredGroups.Trim($TrimChar))" | select -Unique ) } else { @($iniFile.($String).NotRequiredGroups) } } else { $null }
						'NotRequiredRecursiveGroups' = if ($iniFile.($String).NotRequiredRecursiveGroups -ne $null) { if ($iniFile.($String).NotRequiredRecursiveGroups -match ",") { @(iex "echo $($iniFile.($String).NotRequiredRecursiveGroups.Trim($TrimChar))" | select -Unique ) } else { @($iniFile.($String).NotRequiredRecursiveGroups -replace '"', "") } } else { $null }
						'DisableNameorIDCheckingForGroupRules' = switch($iniFile.($String).DisableNameorIDCheckingForGroupRules){"TRUE" {$true}; 1 {$true}; default {$false}}
						'DontAllowMissingNotRequiredGroups' = switch($iniFile.($String).DontAllowMissingNotRequiredGroups){"TRUE" {$true}; 1 {$true}; default {$false}}
						'IncludeDistributionGroups' = switch($iniFile.($String).IncludeDistributionGroupsInGroupRules){"TRUE" {$true}; 1 {$true}; default {$true}}
						'DomainFilter' = if ($iniFile.($String).DomainFilter -ne $null) { if ($iniFile.($String).DomainFilter -match ",") {@(iex "echo $($iniFile.($String).DomainFilter.Trim($TrimChar))" | select -Unique ) } else { @($iniFile.($String).DomainFilter -replace '"', "") } } else { $null }
						'HasADOURules' = if ($iniFile.($String).RequiredOrOUs -or $iniFile.($String).NotRequiredOUs ) { $true } else { $false }
						'RequiredOrOUs' = if ($iniFile.($String).RequiredOrOUs -ne $null) { if ($iniFile.($String).RequiredOrOUs -match ",") { @(iex "echo $($iniFile.($String).RequiredOrOUs.Trim($TrimChar))" | select -Unique | Sort ) } else { @($iniFile.($String).RequiredOrOUs -replace '"', "") } } else { if ($iniFile.($String).NotRequiredOUs -ne $null) { "*" } else { $null } }						
						'NotRequiredOUs' = if ($iniFile.($String).NotRequiredOUs -ne $null) { if ($iniFile.($String).NotRequiredOUs -match ",") { @(iex "echo $($iniFile.($String).NotRequiredOUs.Trim($TrimChar))" | select -Unique | Sort ) } else { @($iniFile.($String).NotRequiredOUs -replace '"', "") } } else { $null }
						'SettingsNumber' = [int]$a
						'UserType' = 'Teacher'
					}
				}
			}
		}
	}
} else {
	$Information +=	"Teacher Filtered Policy Processing Disabled by Functionality Control"
	write-host "Teacher Filtered Policy Processing Disabled by Functionality Control" -foregroundcolor white
}

if ($iniFile.FunctionControl.TurnOffStudentFilteredPolicyFunctionality -ne $true -and $iniFile.FunctionControl.TurnOffStudentFilteredPolicyFunctionality -ne 1) {
	for ($a=0; $a -le $MaxSettingsGroups; $a++) {

		$warning=$false
		$String="StudentTeamsPolicies"+[string]$a
		# Set Policies - Student
		if (($iniFile.($String).ApplyTeamsPolicies -eq $true -or $iniFile.($String).ApplyTeamsPolicies -eq 1) -and !($iniFile.($String).TeamsMessagingPolicy) -and !($iniFile.($String).TeamsMeetingPolicy) -and !($iniFile.($String).TeamsAppSetupPolicy) -and !($iniFile.($String).TeamsCallingPolicy) -and !($iniFile.($String).TeamsMeetingBroadcastPolicy) -and !($iniFile.($String).TeamsChannelsPolicy) -and !($iniFile.($String).TeamsAppPermissionPolicy)  -and !($iniFile.($String).TeamsFeedbackPolicy)) {
			#$ExpectedPolicyActions +=1
			$MajorErrors += "- Check the [$String] Policies settings. No Policies names have been supplied"
			$warning = $true
		}
		
		if ($warning -eq $false) {
			foreach ($PolicyItem in $PolicyList) {
				if (($iniFile.($String).ApplyTeamsPolicies -eq $true -or $iniFile.($String).ApplyTeamsPolicies -eq 1) -and $iniFile.($String).($PolicyItem)) {
					$ExpectedPolicyActions +=1
					$NeedsStudentList = $true
					$ApplyOnlyIfExisting="OnlyChange" + [string]$PolicyItem + "IfBlank"
					$PoliciesToProcess += [PSCustomObject]@{
						'Settings' = "[StudentTeamsPolicies"+[string]$a+"]"
						'Default' = if ($iniFile.($String).CollectionID -eq 10000) { $true } else { switch($iniFile.($String).Default){"TRUE" {$true}; 1 {$true}; default {$false}} }
						'CollectionID' = switch($iniFile.($String).Default){"TRUE" {10000}; 1 {10000}; default { if ($iniFile.($String).CollectionID -ne $null) { if($iniFile.($String).CollectionID -as [int] -gt -1 -and $iniFile.($String).CollectionID -as [int] -le 10000) { $iniFile.($String).CollectionID -as [int] } else { -1 } } else { 100 }}}
						'CollectionIDIgnoreThreshold' = switch($iniFile.($String).Default){"TRUE" {0}; 1 {0}; default { if ($iniFile.($String).CollectionIDIgnoreThreshold -ne $null) { if($iniFile.($String).CollectionIDIgnoreThreshold -as [int] -gt -1 -and $iniFile.($String).CollectionIDIgnoreThreshold -as [int] -le 10000) { $iniFile.($String).CollectionIDIgnoreThreshold -as [int] } else { -1 } } else { 0 }}}
						'CollectionIDsToExclude' = switch($iniFile.($String).Default){"TRUE" { $null }; 1 { $null }; default {if ($iniFile.($String).CollectionIDsToExclude -ne $null) { [int[]](NumericalListCreator $iniFile.($String).CollectionIDsToExclude) } else { $null }}}
						'CollectionIDsToExclude_Display' = switch($iniFile.($String).Default){"TRUE" { $null }; 1 { $null }; default {if ($iniFile.($String).CollectionIDsToExclude -ne $null) { @(NumericalListConc $iniFile.($String).CollectionIDsToExclude) } else { $null }}}
						'Description' = if ($iniFile.($String).TeamsPoliciesDescription) { $iniFile.($String).TeamsPoliciesDescription } else { "-" }
						'ActionType' = $PolicyItem
						'ActionPolicy' = $iniFile.($String).($PolicyItem)
						'ApplyOnlyIfExistingPolicyBlank' = switch($iniFile.($String).($ApplyOnlyIfExisting)){"FALSE" {$false}; 0 {$false}; default {$true}}
						'ApplyToAll' = switch($iniFile.($String).ApplyTeamsPoliciestoAllStudents){"TRUE" {$true}; 1 {$true}; default {$false}}
						'ActionFilter' = $iniFile.($String).Filter
						'FilterType' = if ($UseLocalAttributeFiltering -eq $true) { "Local" } else { "Remote" }
						'FilterSource' = if ($UseLocalAttributeFiltering -eq $true) { if ($iniFile.($String).FilterSource) { switch($iniFile.($String).FilterSource.Trim($TrimChar)){ "Azure" { "AzureAD" }; "AzureAD" { "AzureAD" }; "AD" { "AzureAD" }; "CS" { "CSOnline" }; "CSO" { "CSOnline" }; "CSOnline" { "CSOnline" }; default { if ($UseAzureADforLocalAttributeFilteringByDefault -eq $true) { "AzureAD" } else { "CSOnline" } }} } else { if ($UseAzureADforLocalAttributeFilteringByDefault -eq $true) { "AzureAD" } else { "CSOnline" } } } else { "CSOnline" }
						'FilterIsOK' = if ($iniFile.($String).Filter) { Test-Filter -Codeblock $iniFile.($String).Filter -Local $UseLocalAttributeFiltering } else { $null }
						'OnlyApplyToUnlicensed' = switch($iniFile.($String).OnlyApplytoUnlicensedStudents){"TRUE" {$true}; 1 {$true}; default {$false}}
						'SkipUpdatedLicensingCheck' = switch($iniFile.($String).SkipUpdatedLicensingCheck){"TRUE" {$true}; 1 {$true}; default {$false}}
						'ProcessDisabledUsers' = switch($iniFile.($String).ProcessDisabledUsers){"TRUE" {$true}; 1 {$true}; default {$false}}
						'RemoveFailedUsersFromLicensingGroup' = (@(if ($iniFile.($String).RemoveFailedUsersFromLicensingGroup -ne $null) { if ($iniFile.($String).RemoveFailedUsersFromLicensingGroup.Trim($TrimChar) -ne $null -and $iniFile.($String).RemoveFailedUsersFromLicensingGroup.Trim($TrimChar) -ne "") { @(iex "echo $($iniFile.($String).RemoveFailedUsersFromLicensingGroup.Trim($TrimChar))") | select -Unique | % { switch($_){0 {[int]-99}; "False" {[int]-99}; 1 {[int]14}; 2 {[int]2}; "A" {[int]2}; "Assignment" {[int]2}; "AssignmentError" {[int]2}; "AssignmentErrors" {[int]2}; "Error" {[int]2}; "Errors" {[int]2}; 4 {[int]4}; "Disabled" {[int]4}; "D" {[int]4}; "NotEnabled" {[int]4}; 8 {[int]8}; "N" {[int]8}; "NotLicensed" {[int]8}; "NoLicense" {[int]8}; 6 {[int]2,[int]4}; 10 {[int]2,[int]8}; 12 {[int]4,[int]8}; 14 {[int]14}; "All" {[int]14}; "True" {[int]14}; default {[int]0}} } }  else { [int]0 } } else { [int]0 } ) | select -Unique | select -first 10 | Measure-Object -sum).sum | % {if ($_ -gt 14) { 14 } else { if ($_ -lt 0) { 0 } else { $_ } }}
						'OnFailureApplytoAllUsersOnNextRun' = (@(if ($iniFile.($String).OnFailureApplytoAllUsersOnNextRun -ne $null) { if ($iniFile.($String).OnFailureApplytoAllUsersOnNextRun.Trim($TrimChar) -ne $null -and $iniFile.($String).OnFailureApplytoAllUsersOnNextRun.Trim($TrimChar) -ne "") { @(iex "echo $($iniFile.($String).OnFailureApplytoAllUsersOnNextRun.Trim($TrimChar))") | select -Unique | % { switch($_){0 {[int]-99}; "False" {[int]-99}; 1 {[int]14};2 {[int]2}; "A" {[int]2}; "Assignment" {[int]2}; "AssignmentError" {[int]2}; "AssignmentErrors" {[int]2}; "Error" {[int]2}; "Errors" {[int]2}; 4 {[int]4}; "Disabled" {[int]4}; "D" {[int]4}; "NotEnabled" {[int]4}; 8 {[int]8}; "N" {[int]8}; "NotLicensed" {[int]8}; "NoLicense" {[int]8}; 6 {[int]2,[int]4}; 10 {[int]2,[int]8}; 12 {[int]4,[int]8}; 14 {[int]14}; "All" {[int]14}; "True" {[int]14}; default {[int]0}} } } else { [int]0 } } else { [int]0 } ) | select -Unique | select -first 10 | Measure-Object -sum).sum | % {if ($_ -gt 14) { 14 } else { if ($_ -lt 0) { 0 } else { $_ } }}
						'HasGroupRules' = if ($iniFile.($String).RequiredGroups -or $iniFile.($String).RequiredRecursiveGroups -or $iniFile.($String).RequiredOrGroups -or $iniFile.($String).RequiredOrRecursiveGroups -or $iniFile.($String).NotRequiredGroups -or $iniFile.($String).NotRequiredRecursiveGroups) { $true } else { $false }
						'RequiredGroups' = if ($iniFile.($String).RequiredGroups -ne $null) { if ($iniFile.($String).RequiredGroups -match ",") {@(iex "echo $($iniFile.($String).RequiredGroups.Trim($TrimChar))" | select -Unique ) } else { @($iniFile.($String).RequiredGroups -replace '"', "") } } else { $null }
						'RequiredRecursiveGroups' = if ($iniFile.($String).RequiredRecursiveGroups -ne $null) { if ($iniFile.($String).RequiredRecursiveGroups -match ",") { @(iex "echo $($iniFile.($String).RequiredRecursiveGroups.Trim($TrimChar))" | select -Unique ) } else { @($iniFile.($String).RequiredRecursiveGroups -replace '"', "") } } else { $null }
						'RequiredOrGroups' = if ($iniFile.($String).RequiredOrGroups -ne $null) { if ($iniFile.($String).RequiredOrGroups -match ",") {@(iex "echo $($iniFile.($String).RequiredOrGroups.Trim($TrimChar))" | select -Unique ) } else { @($iniFile.($String).RequiredOrGroups -replace '"', "") } } else { $null }
						'RequiredOrRecursiveGroups' = if ($iniFile.($String).RequiredOrRecursiveGroups -ne $null) { if ($iniFile.($String).RequiredOrRecursiveGroups -match ",") { @(iex "echo $($iniFile.($String).RequiredOrRecursiveGroups.Trim($TrimChar))" | select -Unique ) } else { @($iniFile.($String).RequiredOrRecursiveGroups -replace '"', "") } } else { $null }
						'NotRequiredGroups' = if ($iniFile.($String).NotRequiredGroups -ne $null) { if ($iniFile.($String).NotRequiredGroups -match ",") { @(iex "echo $($iniFile.($String).NotRequiredGroups.Trim($TrimChar))" | select -Unique ) } else { @($iniFile.($String).NotRequiredGroups) } } else { $null }
						'NotRequiredRecursiveGroups' = if ($iniFile.($String).NotRequiredRecursiveGroups -ne $null) { if ($iniFile.($String).NotRequiredRecursiveGroups -match ",") { @(iex "echo $($iniFile.($String).NotRequiredRecursiveGroups.Trim($TrimChar))" | select -Unique ) } else { @($iniFile.($String).NotRequiredRecursiveGroups -replace '"', "") } } else { $null }
						'DisableNameorIDCheckingForGroupRules' = switch($iniFile.($String).DisableNameorIDCheckingForGroupRules){"TRUE" {$true}; 1 {$true}; default {$false}}
						'DontAllowMissingNotRequiredGroups' = switch($iniFile.($String).DontAllowMissingNotRequiredGroups){"TRUE" {$true}; 1 {$true}; default {$false}}
						'IncludeDistributionGroups' = switch($iniFile.($String).IncludeDistributionGroupsInGroupRules){"TRUE" {$true}; 1 {$true}; default {$true}}
						'DomainFilter' = if ($iniFile.($String).DomainFilter -ne $null) { if ($iniFile.($String).DomainFilter -match ",") {@(iex "echo $($iniFile.($String).DomainFilter.Trim($TrimChar))" | select -Unique ) } else { @($iniFile.($String).DomainFilter -replace '"', "") } } else { $null }
						'HasADOURules' = if ($iniFile.($String).RequiredOrOUs -or $iniFile.($String).NotRequiredOUs ) { $true } else { $false }
						'RequiredOrOUs' = if ($iniFile.($String).RequiredOrOUs -ne $null) { if ($iniFile.($String).RequiredOrOUs -match ",") { @(iex "echo $($iniFile.($String).RequiredOrOUs.Trim($TrimChar))" | select -Unique | Sort ) } else { @($iniFile.($String).RequiredOrOUs -replace '"', "") } } else { if ($iniFile.($String).NotRequiredOUs -ne $null) { "*" } else { $null } }						
						'NotRequiredOUs' = if ($iniFile.($String).NotRequiredOUs -ne $null) { if ($iniFile.($String).NotRequiredOUs -match ",") { @(iex "echo $($iniFile.($String).NotRequiredOUs.Trim($TrimChar))" | select -Unique | Sort ) } else { @($iniFile.($String).NotRequiredOUs -replace '"', "") } } else { $null }
						'SettingsNumber' = [int]$a
						'UserType' = 'Student'
					}
				}
			}
		}
	}
} else {
	$Information +=	"Student Filtered Policy Processing Disabled by Functionality Control"
	write-host "Student Filtered Policy Processing Disabled by Functionality Control" -foregroundcolor white
}

$PoliciesToProcess = @($PoliciesToProcess | Sort @{Expression = "UserType"; Descending = $true},@{Expression = "CollectionID"; Descending = $false},@{Expression = "SettingsNumber"; Descending = $false})

if (( @( $PoliciesToProcess | Where {$_.UserType -eq 'Teacher' -and $_.ApplyToAll -eq $false} ).count -gt 0) -and $ApplytoAllTeachers -eq $false) {
    $NeedsTeacherLicensingList= $true
}

if (( @( $PoliciesToProcess | Where {$_.UserType -eq 'Student' -and $_.ApplyToAll -eq $false} ).count -gt 0) -and $ApplytoAllStudents -eq $false) {
    $NeedsStudentLicensingList= $true
}

if (( @( $PackagesToProcess | Where {$_.UserType -eq 'Teacher' -and $_.ApplyToAll -eq $false} ).count -gt 0) -and $ApplytoAllTeachers -eq $false) {
    $NeedsTeacherLicensingList= $true
}

if (( @( $PackagesToProcess | Where {$_.UserType -eq 'Student' -and $_.ApplyToAll -eq $false} ).count -gt 0) -and $ApplytoAllStudents -eq $false) {
    $NeedsStudentLicensingList= $true
}

if (( @( $FilteredSecurityGroupsToProcess | Where {$_.UserType -eq 'Teacher' -and $_.ApplyToAll -eq $false} ).count -gt 0) -and $ApplytoAllTeachers -eq $false) {
    $NeedsTeacherLicensingList= $true
}

if (( @( $FilteredSecurityGroupsToProcess | Where {$_.UserType -eq 'Student' -and $_.ApplyToAll -eq $false} ).count -gt 0) -and $ApplytoAllStudents -eq $false) {
    $NeedsStudentLicensingList= $true
}

if (( @( $FilteredUnifiedGroupsToProcess | Where {$_.UserType -eq 'Teacher' -and $_.ApplyToAll -eq $false} ).count -gt 0) -and $ApplytoAllTeachers -eq $false) {
    $NeedsTeacherLicensingList= $true
}

if (( @( $FilteredUnifiedGroupsToProcess | Where {$_.UserType -eq 'Student' -and $_.ApplyToAll -eq $false} ).count -gt 0) -and $ApplytoAllStudents -eq $false) {
    $NeedsStudentLicensingList= $true
}

if (( @( $FilteredDistributionGroupsToProcess | Where {$_.UserType -eq 'Teacher' -and $_.ApplyToAll -eq $false} ).count -gt 0) -and $ApplytoAllTeachers -eq $false) {
    $NeedsTeacherLicensingList= $true
}

if (( @( $FilteredDistributionGroupsToProcess | Where {$_.UserType -eq 'Student' -and $_.ApplyToAll -eq $false} ).count -gt 0) -and $ApplytoAllStudents -eq $false) {
    $NeedsStudentLicensingList= $true
}

# PoliciesToProcess - HasGroupRules
if ( (@( $PoliciesToProcess  | Where {$_.HasGroupRules -eq $true -or $_.HasADOURules -eq $true} ).count -gt 0)) {
    $NeedsMSONline = $true
	$NeedsAzureADPreview = $true
}
# PackagesToProcess - HasGroupRules
if ( (@( $PackagesToProcess  | Where {$_.HasGroupRules -eq $true -or $_.HasADOURules -eq $true} ).count -gt 0)) {
    $NeedsMSONline = $true
	$NeedsAzureADPreview = $true
}
# FilteredSecurityGroupsToProcess - HasGroupRules
if ( (@( $FilteredSecurityGroupsToProcess  | Where {$_.HasGroupRules -eq $true -or $_.HasADOURules -eq $true} ).count -gt 0)) {
    $NeedsMSONline = $true
	$NeedsAzureADPreview = $true
}
# FilteredUnifiedGroupsToProcess - HasGroupRules
if ( (@( $FilteredUnifiedGroupsToProcess  | Where {$_.HasGroupRules -eq $true -or $_.HasADOURules -eq $true} ).count -gt 0)) {
    $NeedsMSONline = $true
	$NeedsAzureADPreview = $true
}
# FilteredDistributionGroupsToProcess - HasGroupRules
if ( (@( $FilteredDistributionGroupsToProcess  | Where {$_.HasGroupRules -eq $true -or $_.HasADOURules -eq $true} ).count -gt 0)) {
    $NeedsMSONline = $true
	$NeedsAzureADPreview = $true
}

# Get All User Policy Information
$TestGroupRules = @()
$TestADOURules = @()
if ($iniFile.InformationRetrieval.GetAllUserPolicyInformation -eq $true -or $iniFile.InformationRetrieval.GetAllUserPolicyInformation -eq 1) {
    $TestGroupRules += [PSCustomObject]@{
	    'HasGroupRules' = if ($iniFile.InformationRetrieval.RequiredGroups -or $iniFile.InformationRetrieval.RequiredRecursiveGroups -or $iniFile.InformationRetrieval.RequiredOrGroups -or $iniFile.InformationRetrieval.RequiredOrRecursiveGroups -or $iniFile.InformationRetrieval.NotRequiredGroups -or $iniFile.InformationRetrieval.NotRequiredRecursiveGroups) { $true } else { $false }
	    'RequiredGroups' = if ($iniFile.InformationRetrieval.RequiredGroups -ne $null) { if ($iniFile.InformationRetrieval.RequiredGroups -match ",") { @(iex "echo $($iniFile.InformationRetrieval.RequiredGroups.Trim($TrimChar))" | select -Unique) } else { @($iniFile.InformationRetrieval.RequiredGroups -replace '"', "") } } else { $null }
	    'RequiredRecursiveGroups' = if ($iniFile.InformationRetrieval.RequiredRecursiveGroups -ne $null) { if ($iniFile.InformationRetrieval.RequiredRecursiveGroups -match ",") { @(iex "echo $($iniFile.InformationRetrieval.RequiredRecursiveGroups.Trim($TrimChar))" | select -Unique) } else { @($iniFile.InformationRetrieval.RequiredRecursiveGroups -replace '"', "") } } else { $null }
	    'RequiredOrGroups' = if ($iniFile.InformationRetrieval.RequiredOrGroups -ne $null) { if ($iniFile.InformationRetrieval.RequiredOrGroups -match ",") { @(iex "echo $($iniFile.InformationRetrieval.RequiredOrGroups.Trim($TrimChar))" | select -Unique) } else { @($iniFile.InformationRetrieval.RequiredOrGroups -replace '"', "") } } else { $null }
	    'RequiredOrRecursiveGroups' = if ($iniFile.InformationRetrieval.RequiredOrRecursiveGroups -ne $null) { if ($iniFile.InformationRetrieval.RequiredOrRecursiveGroups -match ",") { @(iex "echo $($iniFile.InformationRetrieval.RequiredOrRecursiveGroups.Trim($TrimChar))" | select -Unique) } else { @($iniFile.InformationRetrieval.RequiredOrRecursiveGroups -replace '"', "") } } else { $null }
	    'NotRequiredGroups' = if ($iniFile.InformationRetrieval.NotRequiredGroups -ne $null) { if ($iniFile.InformationRetrieval.NotRequiredGroups -match ",") { @(iex "echo $($iniFile.InformationRetrieval.NotRequiredGroups.Trim($TrimChar))" | select -Unique) } else { @($iniFile.InformationRetrieval.NotRequiredGroups) } } else { $null }
	    'NotRequiredRecursiveGroups' = if ($iniFile.InformationRetrieval.NotRequiredRecursiveGroups -ne $null) { if ($iniFile.InformationRetrieval.NotRequiredRecursiveGroups -match ",") { @(iex "echo $($iniFile.InformationRetrieval.NotRequiredRecursiveGroups.Trim($TrimChar))" | select -Unique) } else { @($iniFile.InformationRetrieval.NotRequiredRecursiveGroups -replace '"', "") } } else { $null }
        'DisableNameorIDCheckingForGroupRules' = switch($iniFile.InformationRetrieval.DisableNameorIDCheckingForGroupRules){"TRUE" {$true}; 1 {$true}; default {$false}}
        'DontAllowMissingNotRequiredGroups' = switch($iniFile.InformationRetrieval.DontAllowMissingNotRequiredGroups){"TRUE" {$true}; 1 {$true}; default {$false}}
        'IncludeDistributionGroups' = switch($iniFile.InformationRetrieval.IncludeDistributionGroupsInGroupRules){"TRUE" {$true}; 1 {$true}; default {$false}}
		'DomainFilter' = if ($iniFile.InformationRetrieval.DomainFilter -ne $null) { if ($iniFile.InformationRetrieval.DomainFilter -match ",") {@(iex "echo $($iniFile.InformationRetrieval.DomainFilter.Trim($TrimChar))" | select -Unique ) } else { @($iniFile.InformationRetrieval.DomainFilter -replace '"', "") } } else { $null }
    }
	
	$TestADOURules = @()
	    $TestADOURules += [PSCustomObject]@{
	    'HasADOURules' = if ($iniFile.InformationRetrieval.RequiredOrOUs -or $iniFile.InformationRetrieval.NotRequiredOUs ) { $true } else { $false }
	    'RequiredOrOUs' = if ($iniFile.InformationRetrieval.RequiredOrOUs -ne $null) { if ($iniFile.InformationRetrieval.RequiredOrOUs -match ",") { @(iex "echo $($iniFile.InformationRetrieval.RequiredOrOUs.Trim($TrimChar))" | select -Unique | Sort ) } else { @($iniFile.InformationRetrieval.RequiredOrOUs -replace '"', "") } } else { if ($iniFile.InformationRetrieval.NotRequiredOUs -ne $null) { "*" } else { $null } }
	    'NotRequiredOUs' = if ($iniFile.InformationRetrieval.NotRequiredOUs -ne $null) { if ($iniFile.InformationRetrieval.NotRequiredOUs -match ",") { @(iex "echo $($iniFile.InformationRetrieval.NotRequiredOUs.Trim($TrimChar))" | select -Unique | Sort ) } else { @($iniFile.InformationRetrieval.NotRequiredOUs -replace '"', "") } } else { $null }
    }
	
    #if ( (@( $TestGroupRules  | Where {$_.HasGroupRules -eq $true} ).count -gt 0)) {   # 1.0.52
    #    $NeedsMSONline = $true
	#    $NeedsAzureADPreview = $true
    #}
}


if ($NeedsStudentLicensingList -eq $true ) {
    $NeedsStudentList = $true
}

if ($NeedsTeacherLicensingList -eq $true ) {
    $NeedsTeacherList = $true
}

if ($NeedsTeacherList -eq $true -or $NeedsStudentList -eq $true) {
    $NeedsMSONline = $true
}

if ($PoliciesToProcess.Count -gt 0) {
    $NeedsMicrosoftTeams = $true

    if ($iniFile.Misc.CreateAzureADAccessPolicy -eq $true -or $iniFile.Misc.CreateAzureADAccessPolicy -eq 1) {
       $NeedsAzureADPreview = $true
    }
}

if ($PackagesToProcess.Count -gt 0) {
    $NeedsMicrosoftTeams = $true

    if ($iniFile.Misc.CreateAzureADAccessPolicy -eq $true -or $iniFile.Misc.CreateAzureADAccessPolicy -eq 1) {
       $NeedsAzureADPreview = $true
    }
}

# 1.0.17 fix
if ($FilteredSecurityGroupsToProcess.Count -gt 0) {
    $NeedsMicrosoftTeams = $true
    $NeedsMSONline = $true
#    $NeedsConnecttoExchangeOnline = $true

    if ($iniFile.Misc.CreateAzureADAccessPolicy -eq $true -or $iniFile.Misc.CreateAzureADAccessPolicy -eq 1) {
       $NeedsAzureADPreview = $true
    }
}

if ($FilteredUnifiedGroupsToProcess.Count -gt 0) {
    $NeedsMicrosoftTeams = $true
#    $NeedsMSONline = $true
    $NeedsConnecttoExchangeOnline = $true

    if ($iniFile.Misc.CreateAzureADAccessPolicy -eq $true -or $iniFile.Misc.CreateAzureADAccessPolicy -eq 1) {
       $NeedsAzureADPreview = $true
    }
}

if ($FilteredDistributionGroupsToProcess.Count -gt 0) {
    $NeedsMicrosoftTeams = $true
#    $NeedsMSONline = $true
    $NeedsConnecttoExchangeOnline = $true

    if ($iniFile.Misc.CreateAzureADAccessPolicy -eq $true -or $iniFile.Misc.CreateAzureADAccessPolicy -eq 1) {
       $NeedsAzureADPreview = $true
    }
}


# FilteredSecurityGroupsToProcess - CollectionID
if ( (@( $FilteredSecurityGroupsToProcess | Where {$_.CollectionID -lt 0 -or $_.CollectionID -gt 10000} ).count -gt 0)) {
	$MajorErrors += "- One or more Filtered Security Groups has an invalid CollectionID setting"
	if ($DetailedLoggingEnabled) {
		Add-Content -Path "$LogFile" -Value "`r`nOne or more Filtered Security Groups has an invalid CollectionID setting" -Encoding Unicode
	}
	write-host "One or more Filtered Security Groups has an invalid CollectionID setting" -ForegroundColor red
}
if ( (@( $FilteredSecurityGroupsToProcess | Where {$_.CollectionIDIgnoreThreshold -lt 0 -or $_.CollectionIDIgnoreThreshold -gt 10000} ).count -gt 0)) {
	$MajorErrors += "- One or more Filtered Security Groups has an invalid CollectionIDIgnoreThreshold setting"
	if ($DetailedLoggingEnabled) {
		Add-Content -Path "$LogFile" -Value "`r`nOne or more Filtered Security Groups has an invalid CollectionIDIgnoreThreshold setting" -Encoding Unicode
	}
	write-host "One or more Filtered Security Groups has an invalid CollectionIDIgnoreThreshold setting" -ForegroundColor red
}

# FilteredUnifiedGroupsToProcess - CollectionID
if ( (@( $FilteredUnifiedGroupsToProcess | Where {$_.CollectionID -lt 0 -or $_.CollectionID -gt 10000} ).count -gt 0)) {
	$MajorErrors += "- One or more Filtered Unified Groups has an invalid CollectionID setting"
	if ($DetailedLoggingEnabled) {
		Add-Content -Path "$LogFile" -Value "`r`nOne or more Filtered Unified Groups has an invalid CollectionID setting" -Encoding Unicode
	}
	write-host "One or more Filtered Unified Groups has an invalid CollectionID setting" -ForegroundColor red
}
if ( (@( $FilteredUnifiedGroupsToProcess | Where {$_.CollectionIDIgnoreThreshold -lt 0 -or $_.CollectionIDIgnoreThreshold -gt 10000} ).count -gt 0)) {
	$MajorErrors += "- One or more Filtered Unified Groups has an invalid CollectionIDIgnoreThreshold setting"
	if ($DetailedLoggingEnabled) {
		Add-Content -Path "$LogFile" -Value "`r`nOne or more Filtered Unified Groups has an invalid CollectionIDIgnoreThreshold setting" -Encoding Unicode
	}
	write-host "One or more Filtered Unified Groups has an invalid CollectionIDIgnoreThreshold setting" -ForegroundColor red
}

# FilteredDistributionGroupsToProcess - CollectionID
if ( (@( $FilteredDistributionGroupsToProcess | Where {$_.CollectionID -lt 0 -or $_.CollectionID -gt 10000} ).count -gt 0)) {
	$MajorErrors += "- One or more Filtered Distribution Groups has an invalid CollectionID setting"
	if ($DetailedLoggingEnabled) {
		Add-Content -Path "$LogFile" -Value "`r`nOne or more Filtered Distribution Groups has an invalid CollectionID setting" -Encoding Unicode
	}
	write-host "One or more Filtered Distribution Groups has an invalid CollectionID setting" -ForegroundColor red
}
if ( (@( $FilteredDistributionGroupsToProcess | Where {$_.CollectionIDIgnoreThreshold -lt 0 -or $_.CollectionIDIgnoreThreshold -gt 10000} ).count -gt 0)) {
	$MajorErrors += "- One or more Filtered Distribution Groups has an invalid CollectionIDIgnoreThreshold setting"
	if ($DetailedLoggingEnabled) {
		Add-Content -Path "$LogFile" -Value "`r`nOne or more Filtered Distribution Groups has an invalid CollectionIDIgnoreThreshold setting" -Encoding Unicode
	}
	write-host "One or more Filtered Distribution Groups has an invalid CollectionIDIgnoreThreshold setting" -ForegroundColor red
}

# PackagesToProcess - CollectionID
if ( (@( $PackagesToProcess | Where {$_.CollectionID -lt 0 -or $_.CollectionID -gt 10000} ).count -gt 0)) {
	$MajorErrors += "- One or more Filtered Teams Package settings has an invalid CollectionID setting"
	if ($DetailedLoggingEnabled) {
		Add-Content -Path "$LogFile" -Value "`r`nOne or more Filtered Teams Package settings has an invalid CollectionID setting" -Encoding Unicode
	}
	write-host "One or more Filtered Teams Package settings has an invalid CollectionID setting" -ForegroundColor red
}
if ( (@( $PackagesToProcess | Where {$_.CollectionIDIgnoreThreshold -lt 0 -or $_.CollectionIDIgnoreThreshold -gt 10000} ).count -gt 0)) {
	$MajorErrors += "- One or more Filtered Teams Package settings has an invalid CollectionIDIgnoreThreshold setting"
	if ($DetailedLoggingEnabled) {
		Add-Content -Path "$LogFile" -Value "`r`nOne or more Filtered Teams Package settings has an invalid CollectionIDIgnoreThreshold setting" -Encoding Unicode
	}
	write-host "One or more Filtered Teams Package settings has an invalid CollectionIDIgnoreThreshold setting" -ForegroundColor red
}

# Policies - CollectionID
if ( (@( $PoliciesToProcess | Where {$_.CollectionID -lt 0 -or $_.CollectionID -gt 10000} ).count -gt 0)) {
	$MajorErrors += "- One or more Filtered Teams Policy settings has an invalid CollectionID setting"
	if ($DetailedLoggingEnabled) {
		Add-Content -Path "$LogFile" -Value "`r`nOne or more Filtered Teams Policy settings has an invalid CollectionID setting" -Encoding Unicode
	}
	write-host "One or more Filtered Teams Policy settings has an invalid CollectionID setting" -ForegroundColor red
}
if ( (@( $PoliciesToProcess | Where {$_.CollectionIDIgnoreThreshold -lt 0 -or $_.CollectionIDIgnoreThreshold -gt 10000} ).count -gt 0)) {
	$MajorErrors += "- One or more Filtered Teams Policy settings has an invalid CollectionIDIgnoreThreshold setting"
	if ($DetailedLoggingEnabled) {
		Add-Content -Path "$LogFile" -Value "`r`nOne or more Filtered Teams Policy settings has an invalid CollectionIDIgnoreThreshold setting" -Encoding Unicode
	}
	write-host "One or more Filtered Teams Policy settings has an invalid CollectionIDIgnoreThreshold setting" -ForegroundColor red
}


# Check that any filter strings contain $_. if local filtering is enabled
	
if ( @($FilteredSecurityGroupsToProcess | where {$_.FilterIsOK -eq $false}).count -gt 0) { 
$MajorErrors += "- One or more Filtered Security Groups has an invalid $FilterType filtering string. Check the event log and examine the FilterIsOK column"
	if ($DetailedLoggingEnabled) {
		Add-Content -Path "$LogFile" -Value "`r`nOne or more Filtered Security Groups has an invalid $FilterType filtering string. Check the event log and examine the FilterIsOK column" -Encoding Unicode
	}
write-host "One or more Filtered Security Groups has an invalid $FilterType filtering string. Check the event log and examine the FilterIsOK column" -ForegroundColor red
}
if ( @($FilteredUnifiedGroupsToProcess | where {$_.FilterIsOK -eq $false}).count -gt 0) { 
	$MajorErrors += "- One or more Filtered Unified Groups has an invalid $FilterType filtering string. Check the event log and examine the FilterIsOK column"
	if ($DetailedLoggingEnabled) {
		Add-Content -Path "$LogFile" -Value "`r`nOne or more Filtered Unified Groups has an invalid $FilterType filtering string. Check the event log and examine the FilterIsOK column" -Encoding Unicode
	}
write-host "One or more Filtered Unified Groups has an invalid $FilterType filtering string. Check the event log and examine the FilterIsOK column" -ForegroundColor red
}
if ( @($FilteredDistributionGroupsToProcess | where {$_.FilterIsOK -eq $false}).count -gt 0) { 
$MajorErrors += "- One or more Filtered Distribution Groups has an invalid $FilterType filtering string. Check the event log and examine the FilterIsOK column"
	if ($DetailedLoggingEnabled) {
		Add-Content -Path "$LogFile" -Value "`r`nOne or more Filtered Distribution Groups has an invalid $FilterType filtering string. Check the event log and examine the FilterIsOK column" -Encoding Unicode
	}
write-host "One or more Filtered Distribution Groups has an invalid $FilterType filtering string. Check the event log and examine the FilterIsOK column" -ForegroundColor red
}
if ( @($PackagesToProcess | where {$_.FilterIsOK -eq $false}).count -gt 0) { 
$MajorErrors += "- One or more Filtered Teams Package settings has an invalid $FilterType filtering string. Check the event log and examine the FilterIsOK column"
	if ($DetailedLoggingEnabled) {
		Add-Content -Path "$LogFile" -Value "`r`nOne or more Filtered Teams Package settings has an invalid $FilterType filtering string. Check the event log and examine the FilterIsOK column" -Encoding Unicode
	}
write-host "One or more Filtered Teams Package settings has an invalid $FilterType filtering string. Check the event log and examine the FilterIsOK column" -ForegroundColor red
}	
if ( @($PoliciesToProcess | where {$_.FilterIsOK -eq $false}).count -gt 0) { 
		$MajorErrors += "- One or more Filtered Teams Policy settings has an invalid $FilterType filtering string. Check the event log and examine the FilterIsOK column"
	if ($DetailedLoggingEnabled) {
		Add-Content -Path "$LogFile" -Value "`r`nOne or more Filtered Teams Policy settings has an invalid $FilterType filtering string. Check the event log and examine the FilterIsOK column" -Encoding Unicode
	}
write-host "One or more Filtered Teams Policy settings has an invalid $FilterType filtering string. Check the event log and examine the FilterIsOK column" -ForegroundColor red
}


# Check that any AD filter isnt mutually exclusive
	
if ( @($FilteredSecurityGroupsToProcess | where {$_.HasADOURules -eq $true -and ($_.NotRequiredOUs -contains '*' -or $_.NotRequiredOUs -contains '**')}).count -gt 0) { 
$MajorErrors += "- One or more Filtered Security Groups has Active Directory OU/CN NotRequiredOUs contains wildcards that will filter out all users"
	if ($DetailedLoggingEnabled) {
		Add-Content -Path "$LogFile" -Value "`r`nOne or more Filtered Security Groups Active Directory OU/CN NotRequiredOUs contains wildcards that will filter out all users" -Encoding Unicode
	}
write-host "One or more Filtered Security Groups has Active Directory OU/CN NotRequiredOUs contains wildcards that will filter out all users" -ForegroundColor red
}

if ( @($FilteredUnifiedGroupsToProcess | where {$_.HasADOURules -eq $true -and ($_.NotRequiredOUs -contains '*' -or $_.NotRequiredOUs -contains '**')}).count -gt 0) { 
$MajorErrors += "- One or more Filtered Unified Groups has Active Directory OU/CN NotRequiredOUs contains wildcards that will filter out all users"
	if ($DetailedLoggingEnabled) {
		Add-Content -Path "$LogFile" -Value "`r`nOne or more Filtered Unified Groups has Active Directory OU/CN NotRequiredOUs contains wildcards that will filter out all users" -Encoding Unicode
	}
write-host "One or more Filtered Unified Groups has Active Directory OU/CN NotRequiredOUs contains wildcards that will filter out all users" -ForegroundColor red
}

if ( @($FilteredDistributionGroupsToProcess | where {$_.HasADOURules -eq $true -and ($_.NotRequiredOUs -contains '*' -or $_.NotRequiredOUs -contains '**')}).count -gt 0) { 
$MajorErrors += "- One or more Filtered Distribution Groups has Active Directory OU/CN NotRequiredOUs contains wildcards that will filter out all users"
	if ($DetailedLoggingEnabled) {
		Add-Content -Path "$LogFile" -Value "`r`nOne or more Filtered Distribution Groups has Active Directory OU/CN NotRequiredOUs contains wildcards that will filter out all users" -Encoding Unicode
	}
write-host "One or more Filtered Distribution Groups has Active Directory OU/CN NotRequiredOUs contains wildcards that will filter out all users" -ForegroundColor red
}

if ( @($PackagesToProcess | where {$_.HasADOURules -eq $true -and ($_.NotRequiredOUs -contains '*' -or $_.NotRequiredOUs -contains '**')}).count -gt 0) { 
$MajorErrors += "- One or more Filtered Teams Package settings has Active Directory OU/CN NotRequiredOUs contains wildcards that will filter out all users"
	if ($DetailedLoggingEnabled) {
		Add-Content -Path "$LogFile" -Value "`r`nOne or more Filtered Teams Package settings has Active Directory OU/CN NotRequiredOUs contains wildcards that will filter out all users" -Encoding Unicode
	}
write-host "One or more Filtered Teams Package settings has Active Directory OU/CN NotRequiredOUs contains wildcards that will filter out all users" -ForegroundColor red
}

if ( @($PoliciesToProcess | where {$_.HasADOURules -eq $true -and ($_.NotRequiredOUs -contains '*' -or $_.NotRequiredOUs -contains '**')}).count -gt 0) { 
$MajorErrors += "- One or more Filtered Teams Policy settings has Active Directory OU/CN NotRequiredOUs contains wildcards that will filter out all users"
	if ($DetailedLoggingEnabled) {
		Add-Content -Path "$LogFile" -Value "`r`nOne or more Filtered Teams Policy settings has Active Directory OU/CN NotRequiredOUs contains wildcards that will filter out all users" -Encoding Unicode
	}
write-host "One or more Filtered Teams Policy settings has Active Directory OU/CN NotRequiredOUs contains wildcards that will filter out all users" -ForegroundColor red
}


if ($FilteredSecurityGroupsToProcess.Count -ne $ExpectedSecurityGroupActions) {
	$MajorErrors += "- An error was detected in the processing of Filtered Security Group settings: $($FilteredSecurityGroupsToProcess.Count) settings processed OK /$ExpectedSecurityGroupActions settings found"
	if ($DetailedLoggingEnabled) {
		Add-Content -Path "$LogFile" -Value "`r`nAn error was detected in the processing of Filtered Security Group settings: $($FilteredSecurityGroupsToProcess.Count) settings processed OK/$ExpectedSecurityGroupActions settings found" -Encoding Unicode
	}
	write-host "An error was detected in the processing of Filtered Security Group settings: $($FilteredSecurityGroupsToProcess.Count) settings processed OK/$ExpectedSecurityGroupActions settings found" -ForegroundColor red
}


if ($FilteredUnifiedGroupsToProcess.Count -ne $ExpectedUnifiedGroupActions) {
	$MajorErrors += "- An error was detected in the processing of Filtered Unified Group settings: $($FilteredUnifiedGroupsToProcess.Count) settings processed OK/$ExpectedUnifiedGroupActions settings found"
	if ($DetailedLoggingEnabled) {
		Add-Content -Path "$LogFile" -Value "`r`nAn error was detected in the processing of Filtered Unified Group settings: $($FilteredUnifiedGroupsToProcess.Count) settings processed OK/$ExpectedUnifiedGroupActions settings found" -Encoding Unicode
	}
	write-host "An error was detected in the processing of Filtered Unified Group settings: $($FilteredUnifiedGroupsToProcess.Count) settings processed OK/$ExpectedUnifiedGroupActions settings found" -ForegroundColor red
}


if ($FilteredDistributionGroupsToProcess.Count -ne $ExpectedDistributionGroupActions) {
	$MajorErrors += "- An error was detected in the processing of Filtered Distribution Group settings: $($FilteredDistributionGroupsToProcess.Count) settings processed OK/$ExpectedDistributionGroupActions settings found"
	if ($DetailedLoggingEnabled) {
		Add-Content -Path "$LogFile" -Value "`r`nAn error was detected in the processing of Filtered Distribution Group settings: $($FilteredDistributionGroupsToProcess.Count) settings processed OK/$ExpectedDistributionGroupActions settings found" -Encoding Unicode
	}
	write-host "An error was detected in the processing of Filtered Distribution Group settings: $($FilteredDistributionGroupsToProcess.Count) settings processed OK/$ExpectedDistributionGroupActions settings found" -ForegroundColor red
}


if ($PackagesToProcess.Count -ne $ExpectedPackageActions) {
	$MajorErrors += "- An error was detected in the processing of Teams Package settings: $($PackagesToProcess.Count) settings processed OK/$ExpectedPackageActions settings found"
	if ($DetailedLoggingEnabled) {
		Add-Content -Path "$LogFile" -Value "`r`nAn error was detected in the processing of Teams Package settings: $($PackagesToProcess.Count) settings processed OK/$ExpectedPackageActions settings found" -Encoding Unicode
	}
	write-host "An error was detected in the processing of Teams Package settings: $($PackagesToProcess.Count) settings processed OK/$ExpectedPackageActions settings found" -ForegroundColor red
}

if ($PoliciesToProcess.Count -ne $ExpectedPolicyActions) {
	$MajorErrors += "- An error was detected in the processing of Teams Policy settings: $($PoliciesToProcess.Count) settings processed OK/$ExpectedPolicyActions settings found"
	if ($DetailedLoggingEnabled) {
		Add-Content -Path "$LogFile" -Value "`r`nAn error was detected in the processing of Teams Policy settings: $($PoliciesToProcess.Count) settings processed OK/$ExpectedPolicyActions settings found" -Encoding Unicode
	}
	write-host "An error was detected in the processing of Teams Policy settings: $($PoliciesToProcess.Count) settings processed OK/$ExpectedPolicyActions settings found" -ForegroundColor red
}


# Information Retrieval - Get Policy Jobs
if ($iniFile.InformationRetrieval.GetPolicyJobs -eq $true -or $iniFile.InformationRetrieval.GetPolicyJobs -eq 1) {
   $NeedsMicrosoftTeams = $true
}

if ($UseLocalAttributeFiltering -eq $true -and $NeedsMicrosoftTeams -eq $true ) {
	$NeedsAzureADPreview = $true
}


# Process Source Groups
foreach ($UserType in @('TeacherSourceGroups','StudentSourceGroups')) {
	foreach ($Setting in @('RequiredGroups','RequiredRecursiveGroups','RequiredOrGroups','RequiredOrRecursiveGroups','NotRequiredGroups','NotRequiredRecursiveGroups','DomainFilter')) {
		if ($iniFile.($UserType).($Setting) -ne $null) { $iniFile.($UserType).($Setting) = if ($iniFile.($UserType).($Setting) -match ",") {@(iex "echo $($iniFile.($UserType).($Setting).Trim($TrimChar))" | select -Unique ) } else { @($iniFile.($UserType).($Setting) -replace '"', "") }}
	}
	if ($iniFile.($UserType).RequiredGroups -eq $null -and $iniFile.($UserType).RequiredRecursiveGroups -eq $null -and $iniFile.($UserType).RequiredOrGroups -eq $null -and $iniFile.($UserType).RequiredOrRecursiveGroups -eq $null -and ( $iniFile.($UserType).NotRequiredGroups -ne $null -or $iniFile.($UserType).NotRequiredRecursiveGroups -ne $null )) {
		$iniFile.($UserType).RequiredOrGroups = '*'
	}

	if ($iniFile.($UserType).RequiredOrGroups -contains '*' -or $iniFile.($UserType).RequiredOrRecursiveGroups -contains '*') {
		$iniFile.($UserType).RequiredOrGroups = '*'
		$iniFile.($UserType).RequiredOrRecursiveGroups = '*'
	}	

}

$SourceGroupDefaults = @([pscustomobject]@{Setting= 'DontProcessStudentsFoundinTeacherSourceGroups'; DefaultValue = $false},
[pscustomobject]@{Setting= 'DisableNameorIDCheckingForGroupRules'; DefaultValue = $false},
[pscustomobject]@{Setting= 'IncludeDistributionGroupsInGroupRules'; DefaultValue = $true},
[pscustomobject]@{Setting= 'LogAvailableUsers'; DefaultValue = $false}
)

foreach ($Setting in $SourceGroupDefaults) {
	if ($iniFile.TeacherSourceGroups.($Setting.Setting) -eq $null) {
		$iniFile.TeacherSourceGroups.($Setting.Setting) = ($Setting.DefaultValue)
	}
}

$SourceGroupDefaults = @([pscustomobject]@{Setting= 'DontProcessTeachersFoundinStudentSourceGroups'; DefaultValue = $false},
[pscustomobject]@{Setting= 'DisableNameorIDCheckingForGroupRules'; DefaultValue = $false},
[pscustomobject]@{Setting= 'IncludeDistributionGroupsInGroupRules'; DefaultValue = $true},
[pscustomobject]@{Setting= 'LogAvailableUsers'; DefaultValue = $false}
)

foreach ($Setting in $SourceGroupDefaults) {
	if ($iniFile.StudentSourceGroups.($Setting.Setting) -eq $null) {
		$iniFile.StudentSourceGroups.($Setting.Setting) = ($Setting.DefaultValue)
	}
}

foreach ($UserType in @('TeacherSourceGroups','StudentSourceGroups')) {
	if ( $iniFile.($UserType).RequiredGroups -eq $null -and $iniFile.($UserType).RequiredRecursiveGroups -eq $null -and $iniFile.($UserType).RequiredOrGroups -eq $null -and $iniFile.($UserType).RequiredOrRecursiveGroups -eq $null) {
		$iniFile.($UserType).HasGroups = $false
	} else {
		$iniFile.($UserType).HasGroups = $true
	}
}

foreach ($Setting in @('HasGroups','RequiredGroups','RequiredRecursiveGroups','RequiredOrGroups','RequiredOrRecursiveGroups','NotRequiredGroups','NotRequiredRecursiveGroups','DisableNameorIDCheckingForGroupRules','IncludeDistributionGroupsInGroupRules','DontProcessStudentsFoundinTeachersSourceGroup','DomainFilter','LogAvailableUsers')) {
	if ($iniFile.TeacherSourceGroups.($Setting) -ne $null) { 
		$SourceGroups += "Teachers SourceGroup $($Setting): " + $iniFile.TeacherSourceGroups.($Setting)
	}
}

foreach ($Setting in @('HasGroups','RequiredGroups','RequiredRecursiveGroups','RequiredOrGroups','RequiredOrRecursiveGroups','NotRequiredGroups','NotRequiredRecursiveGroups','DisableNameorIDCheckingForGroupRules','IncludeDistributionGroupsInGroupRules','DontProcessStudentsFoundinTeachersSourceGroup','DomainFilter','LogAvailableUsers')) {
	if ($iniFile.StudentSourceGroups.($Setting) -ne $null) { 
		$SourceGroups += "Students SourceGroup $($Setting): " + $iniFile.StudentSourceGroups.($Setting)
	}
}

foreach ($UserType in @('TeacherSourceGroups','StudentSourceGroups')) {
	if ($iniFile.($UserType).RequiredOrGroups -contains '*' -or $iniFile.($UserType).RequiredOrRecursiveGroups -contains '*') {
		$iniFile.($UserType).RequiredOrGroups = $null
		$iniFile.($UserType).RequiredOrRecursiveGroups = $null
	}
}

# by default the group engine will select all users if there are no requiredorgroups

if ($NeedsTeacherList -eq $true) {
	if ( $iniFile.TeacherSourceGroups.HasGroups -eq $false ) {
		$MajorErrors += "- Check the TeacherSourceGroups settings. RequiredGroups, RequiredRecursiveGroups, RequiredOrGroups, RequiredOrRecursiveGroups, NotRequiredGroups NotRequiredRecursiveGroups or  must be supplied"
	}
}

if ($NeedsStudentList -eq $true) {
	if ( $iniFile.StudentSourceGroups.HasGroups -eq $false ) {
		$MajorErrors += "- Check the StudentSourceGroups settings. RequiredGroups, RequiredRecursiveGroups, RequiredOrGroups, RequiredOrRecursiveGroups, NotRequiredGroups NotRequiredRecursiveGroups or  must be supplied"
	}
}

if ($iniFile.TeacherSourceGroups.RequiredGroups -contains '*' -or $iniFile.TeacherSourceGroups.RequiredRecursiveGroups -contains '*') {
	$MajorErrors += '- Check the TeacherSourceGroups settings. RequiredGroups or RequiredRecursiveGroups should not contain the * wildcard'
}		
if ($iniFile.TeacherSourceGroups.NotRequiredGroups -contains '*' -or $iniFile.TeacherSourceGroups.NotRequiredRecursiveGroups -contains '*') {
	$MajorErrors += '- Check the TeacherSourceGroups settings. NotRequiredGroups or NotRequiredRecursiveGroups should not contain the * wildcard'
}

if ($iniFile.StudentSourceGroups.RequiredGroups -contains '*' -or $iniFile.StudentSourceGroups.RequiredRecursiveGroups -contains '*') {
	$MajorErrors += '- Check the StudentSourceGroups settings. RequiredGroups or RequiredRecursiveGroups should not contain the * wildcard'
}		
if ($iniFile.StudentSourceGroups.NotRequiredGroups -contains '*' -or $iniFile.StudentSourceGroups.NotRequiredRecursiveGroups -contains '*') {
	$MajorErrors += '- Check the StudentSourceGroups settings. NotRequiredGroups or NotRequiredRecursiveGroups should not contain the * wildcard'
}

if (( $iniFile.LicensingGroups.AssignTeacherstoLicensingGroup -ne $true -and $iniFile.LicensingGroups.AssignTeacherstoLicensingGroup -ne 1) -and $NeedsTeacherLicensingList -eq $true) {
    if ( !( $iniFile.LicensingGroups.AllTeachersLicensingGroupName) ) {
    	$MajorErrors += "- (Required for other functions) Check the Teachers Licensing settings to make sure the Licensing Group Name is configured"
        write-host "(Required for other functions) Check the Teachers Licensing settings to make sure the Licensing Group Name is configured" -ForegroundColor red
        if ($DetailedLoggingEnabled) {
            Add-Content -Path "$LogFile" -Value "(Required for other functions) Check the Teachers Licensing settings to make sure the Licensing Group Name is configured" -Encoding Unicode
        }
	} else {
		$LicensingInformation += "(Required for other functions) Teachers Licensing Licensing Group Name: $($iniFile.LicensingGroups.AllTeachersLicensingGroupName)"
	}
}

if (( $iniFile.LicensingGroups.AssignStudentstoLicensingGroup -ne $true -and $iniFile.LicensingGroups.AssignStudentstoLicensingGroup -ne 1) -and $NeedsStudentLicensingList -eq $true) {
    if ( !( $iniFile.LicensingGroups.AllStudentsLicensingGroupName) ) {
    	$MajorErrors += "- (Required for other functions) Check the Students Licensing settings to make sure the Licensing Group Name is configured"
        write-host "(Required for other functions) Check the Students Licensing settings to make sure the Licensing Group Name is configured" -ForegroundColor red
        if ($DetailedLoggingEnabled) {
            Add-Content -Path "$LogFile" -Value "(Required for other functions) Check the Students Licensing settings to make sure the Licensing Group Name is configured" -Encoding Unicode
        }
	} else {
		$LicensingInformation += "(Required for other functions) Students Licensing Licensing Group Name: $($iniFile.LicensingGroups.AllStudentsLicensingGroupName)"
	}
}


if ($iniFile.InformationRetrieval.GetAllUserPolicyInformation -eq $true -or $iniFile.InformationRetrieval.GetAllUserPolicyInformation -eq 1) {
    $NeedsMicrosoftTeams = $true
    $NeedsMSONline = $true
    $NeedsConnecttoExchangeOnline = $true
    $NeedsAzureADPreview = $true
    if ($iniFile.TeacherSourceGroups.HasGroups -eq $true) { # 1.0.97
		$NeedsTeacherList = $true
	}
    if ($iniFile.StudentSourceGroups.HasGroups -eq $true) { # 1.0.97
		$NeedsStudentList = $true
	}
	if ($iniFile.InformationRetrieval.Filter) {
		if ($UseLocalAttributeFiltering -eq $true) {
			$string = "Local"
		} else {
			$string = "Online"
		}
		if ($UseLocalAttributeFiltering -eq $true) {
			if ( (Test-Filter -Codeblock $iniFile.InformationRetrieval.Filter -Local $UseLocalAttributeFiltering) -eq $false ) {
				$MajorErrors += "- Get All User Policy Information: the $string filtering string is invalid"
				if ($DetailedLoggingEnabled) {
					Add-Content -Path "$LogFile" -Value "`r`nGet All User Policy Information: the $string filtering string is invalid" -Encoding Unicode
				}
				write-host "Get All User Policy Information: the $string filtering string is invalid" -ForegroundColor red				
			}
		}
	}

	if ( @($TestADOURules | where {$_.HasADOURules -eq $true -and ($_.NotRequiredOUs -contains '*' -or $_.NotRequiredOUs -contains '**')}).count -gt 0) { 
	$MajorErrors += "- Get All User Policy Information: the Active Directory OU/CN NotRequiredOUs contains wildcards and will filter out all users"
		if ($DetailedLoggingEnabled) {
			Add-Content -Path "$LogFile" -Value "`r`nGet All User Policy Information: the Active Directory OU/CN NotRequiredOUs contains wildcards and will filter out all users" -Encoding Unicode
		}
	write-host "Get All User Policy Information: the Active Directory OU/CN NotRequiredOUs contains wildcards and will filter out all users" -ForegroundColor red
	}
}






}


if ($MajorErrors.Count -eq 0) {
	if ( $NeedsAzureADPreview -eq $true -and ($iniFile.Misc.CreateAzureADAccessPolicy -eq $true -or $iniFile.Misc.CreateAzureADAccessPolicy -eq 1)) {
		if ( !($iniFile.Tenancy.OnMicrosoftCOMPrefix) ) {
			$MajorErrors += "- Missing required OnMicrosoftCOMPrefix from the Settings File"
		}
		if ( $iniFile.Tenancy.OnMicrosoftCOMPrefix -eq "myschool" )  {
			$MajorErrors += "- Invalid OnMicrosoftCOMPrefix of $($iniFile.Tenancy.OnMicrosoftCOMPrefix) in the Settings File"
		}
	} else {
		if ( !($iniFile.Tenancy.OnMicrosoftCOMPrefix) ) {
			$Warnings+= += "- Missing OnMicrosoftCOMPrefix from the Settings File"
		}
		if ( $iniFile.Tenancy.OnMicrosoftCOMPrefix -eq "myschool" )  {
			$Warnings+= += "- Invalid OnMicrosoftCOMPrefix of $($iniFile.Tenancy.OnMicrosoftCOMPrefix) in the Settings File"
		}		
	}
}



if ($DetailedLoggingEnabled) {
    Add-Content -Path "$LogFile" -Value "`r`nRun Started: $starttime" -Encoding Unicode
    Add-Content -Path "$LogFile" -Value "Program Location: $Myname" -Encoding Unicode
    Add-Content -Path "$LogFile" -Value "Settings File: $SettingsFilename (last modified $inifilewritetime)" -Encoding Unicode
    Add-Content -Path "$LogFile" -Value "Profile Name: $ProfileName" -Encoding Unicode
    Add-Content -Path "$LogFile" -Value "MakeChanges: $MakeChanges" -Encoding Unicode
	Add-Content -Path "$LogFile" -Value "WhatIf: $WhatIf" -Encoding Unicode
}


if ($MajorErrors.Count -gt 0) {
    write-host
	if ($ProcessingSettings -eq $true) {
		write-host "Please check the settings provided" -ForegroundColor red
		write-host "STEPER cannot continue until the problems with the settings are resolved" -ForegroundColor red
		if ($DetailedLoggingEnabled) {
			Add-Content -Path "$LogFile" -Value "Please check the settings provided`r`nSTEPER cannot continue until the problems with the settings are resolved" -Encoding Unicode
		}
	} else {
		write-host "STEPER cannot continue" -ForegroundColor red
		if ($DetailedLoggingEnabled) {
			Add-Content -Path "$LogFile" -Value "STEPER cannot continue" -Encoding Unicode
		}
	}
}


if ($MajorErrors.Count -eq 0) {
    if ($DetailedLoggingEnabled) {
	    Add-Content -Path "$LogFile" -Value "`r`nChecking System Settings and Modules" -Encoding Unicode
        Add-Content -Path "$LogFile" -Value "====================================`r`n" -Encoding Unicode
    }	
	
	Remove-ItemProperty -Path $regpath -Name 'RemediateSkypeAccessTokenTimeout' -Force -Erroraction SilentlyContinue # 1.0.52

# Only create policy if $NeedsMicrosoftTeams -eq $true	
    if (($iniFile.Misc.CreateAzureADAccessPolicy -eq $true -or $iniFile.Misc.CreateAzureADAccessPolicy -eq 1) -and $NeedsAzureADPreview -eq $true) {
	    #set-RegistryValue -Path $regpath -Name 'RemediateSkypeAccessTokenTimeout' -ValueType DWORD -Value 1
		set-RegistryValue -Path $regpath -Name 'UsingAzureADAccessPolicy' -ValueType DWORD -Value 1
    # 0 not required, or not in use
    # 1 required, or in use
    # 2 required, policy set by this instance / profile, in use
    } else {
		Set-RegistryValue -Path $regpath -Name 'UsingAzureADAccessPolicy' -ValueType DWORD -Value 0
    }
    if ($iniFile.Tenancy.OnMicrosoftCOMPrefix) {
		Set-RegistryValue -Path $regpath -Name 'OnMicrosoftCOMPrefix' -ValueType String -Value $iniFile.Tenancy.OnMicrosoftCOMPrefix
	} else {
		Set-RegistryValue -Path $regpath -Name 'OnMicrosoftCOMPrefix' -ValueType String -Value $iniFile.Credentials.GA_Username.Split('@')[1]
	}
}

#$Actions += "- Processed all settings - $($iniFile.Count) settings groups"
#write-host "Processed all settings - $($iniFile.Count) settings groups" -foregroundcolor green


if ($MajorErrors.Count -eq 0) {
    write-host "Installing and/or updating the required Powershell Modules" -foregroundcolor green
}

[int]$ModuleModifiedCount=0

if ($MajorErrors.Count -eq 0 -and !($iniFile.PowerShell.DisableNuGetCheck -eq $true -or $iniFile.PowerShell.DisableNuGetCheck -eq 1)) {
    #$packproviders = @(get-packageprovider | where {$_.Name -eq "Nuget" -and $_.Version -eq '3.0.0.1'})
	$packproviders = @(get-packageprovider | where {($_.Name -eq "Nuget" -and $_.Version.Major[0] -gt 2 )})   # 1.0.35 added Major
    if ($packproviders.count -eq 0) {
        try {
            Install-PackageProvider -Name NuGet -Force | Out-Null # -RequiredVersion 3.0.0.1 -Force | Out-Null
            $Actions += "- Installed the NuGet Package Provider"
            write-host "Installed the NuGet Package Provider" -ForegroundColor Yellow
            $ModuleModifiedCount += 1
            if ($DetailedLoggingEnabled) {
                Add-Content -Path "$LogFile" -Value "Installed the NuGet Package Provider" -Encoding Unicode
            }
        }
        catch {
            $MajorErrors += "- Unable to Install the NuGet Package Provider"
            $Actions += "- Unable to Install the NuGet Package Provider"
            write-host "Unable to Install the NuGet Package Provider" -ForegroundColor Red
            if ($DetailedLoggingEnabled) {
                Add-Content -Path "$LogFile" -Value "Unable to Install the NuGet Package Provider" -Encoding Unicode
            }
        }
    } else {
        if ($monthlyupdatecheck -eq $true) {
            try {
                Install-PackageProvider -Name NuGet | Out-Null
                $Actions += "- Updated the NuGet Package Provider"
                write-host "Updated the NuGet Package Provider" -ForegroundColor Yellow
                $ModuleModifiedCount += 1
                if ($DetailedLoggingEnabled) {
                    Add-Content -Path "$LogFile" -Value "Updated the NuGet Package Provider" -Encoding Unicode
                }
                }
            catch {
                $Actions += "- Unable to Update the NuGet Package Provider"
                $MajorErrors += "- Unable to Update the NuGet Package Provider"
                write-host "Unable to Update the NuGet Package Provider" -ForegroundColor Red
                if ($DetailedLoggingEnabled) {
                    Add-Content -Path "$LogFile" -Value "Unable to Update the NuGet Package Provider" -Encoding Unicode
                }
                }
        } else {
            $Actions += "- Checked that the NuGet Package Provider is present"
        }	  
    }
}


if ($MajorErrors.Count -eq 0 -and !($iniFile.PowerShell.DisablePowerShellGetCheck -eq $true -or $iniFile.PowerShell.DisablePowerShellGetCheck -eq 1)) {
	$packproviders = @(get-module -ListAvailable | where {($_.Name -eq "PowershellGet" -and $_.Version.Major[0] -gt 1 )})
    if ($packproviders.count -eq 0) {
        try {
            Install-Module PowershellGet -Force -AllowClobber -WarningAction SilentlyContinue  | Out-Null # -RequiredVersion 3.0.0.1 -Force | Out-Null
            $MajorErrors += "- Installed the PowershellGet Package Provider. You must start STEPER again in a new Powershell instance for this change to take effect"
            $Actions += "- Installed the PowershellGet Package Provider. You must start STEPER again in a new Powershell instance for this change to take effect"
            write-host "Installed the PowershellGet Package Provider. You must start STEPER again in a new Powershell instance for this change to take effect" -ForegroundColor Red
            $ModuleModifiedCount += 1
            if ($DetailedLoggingEnabled) {
                Add-Content -Path "$LogFile" -Value "Installed the PowershellGet Package Provider. You must start STEPER again in a new Powershell instance for this change to take effect" -Encoding Unicode
            }
        }
        catch {
            $MajorErrors += "- Unable to Install the PowershellGet Package Provider"
            $Actions += "- Unable to Install the PowershellGet Package Provider"
            write-host "Unable to Install the PowershellGet Package Provider" -ForegroundColor Red
            if ($DetailedLoggingEnabled) {
                Add-Content -Path "$LogFile" -Value "Unable to Install the PowershellGet Package Provider" -Encoding Unicode
            }
        }
    } else {
        if ($monthlyupdatecheck -eq $true) {
            try {
                Install-Module PowershellGet -Force -AllowClobber -WarningAction SilentlyContinue | Out-Null
                $Actions += "- Updated the PowershellGet Package Provider"
                write-host "Updated the PowershellGet Package Provider" -ForegroundColor Yellow
                $ModuleModifiedCount += 1
                if ($DetailedLoggingEnabled) {
                    Add-Content -Path "$LogFile" -Value "Updated the PowershellGet Package Provider" -Encoding Unicode
                }
                }
            catch {
                $Actions += "- Unable to Update the PowershellGet Package Provider"
                $MajorErrors += "- Unable to Update the PowershellGet Package Provider"
                write-host "Unable to Update the PowershellGet Package Provider" -ForegroundColor Red
                if ($DetailedLoggingEnabled) {
                    Add-Content -Path "$LogFile" -Value "Unable to Update the PowershellGet Package Provider" -Encoding Unicode
                }
                }
        } else {
            $Actions += "- Checked that the PowershellGet Package Provider is present"
        }	  
    }
}



if ($MajorErrors.Count -eq 0 -and !($iniFile.PowerShell.DisableAzureADPreviewCheck -eq $true -or $iniFile.PowerShell.DisableAzureADPreviewCheck -eq 1)) {
	If ((Get-Module AzureADPreview -ListAvailable) -eq $null){
		#write-host "AzureADPreview module not installed"
		try {
			Install-Module AzureADPreview -Confirm:$False -Force -SkipPublisherCheck -AllowClobber -WarningAction SilentlyContinue
			#write-host "AzureADPreview module now installed"
			$Actions += "- Installed the AzureADPreview Powershell Module"
            write-host "Installed the AzureADPreview Powershell Module" -ForegroundColor Yellow
            if ($DetailedLoggingEnabled) {
                Add-Content -Path "$LogFile" -Value "Installed the AzureADPreview Powershell Module" -Encoding Unicode
            }
            $ModuleModifiedCount += 1
			}
		catch {
			$MajorErrors += "- Unable to Install the AzureADPreview Powershell Module"
            $Actions += "- Unable to Install the AzureADPreview Powershell Module"
            write-host "Unable to Install the AzureADPreview Powershell Module" -ForegroundColor Red
            if ($DetailedLoggingEnabled) {
                Add-Content -Path "$LogFile" -Value "Unable to Install the AzureADPreview Powershell Module" -Encoding Unicode
            }
		}
	} else {
        if ($monthlyupdatecheck -eq $true) {
            try {
                Update-Module AzureADPreview -Confirm:$False -Force -WarningAction SilentlyContinue
                $Actions += "- Updated the AzureADPreview Powershell Module"
                write-host "Updated the AzureADPreview Powershell Module" -ForegroundColor Yellow
                $ModuleModifiedCount += 1
                if ($DetailedLoggingEnabled) {
                    Add-Content -Path "$LogFile" -Value "Updated the AzureADPreview Powershell Module" -Encoding Unicode
                }
                }
            catch {
                $Actions += "- Unable to Update the AzureADPreview Powershell Module"
                $MajorErrors += "- Unable to Update the AzureADPreview Powershell Module"
                write-host "Unable to Update the AzureADPreview Powershell Module" -ForegroundColor Red
                if ($DetailedLoggingEnabled) {
                    Add-Content -Path "$LogFile" -Value "Unable to Update the AzureADPreview Powershell Module" -Encoding Unicode
                }
                }
        } else {
            $Actions += "- Checked that the AzureADPreview Powershell Module is present"
        }
	}
}

if ($MajorErrors.Count -eq 0 -and !($iniFile.PowerShell.DisableMSOnlineCheck -eq $true -or $iniFile.PowerShell.DisableMSOnlineCheck -eq 1)) {
	If ((Get-Module MSOnline -ListAvailable) -eq $null){
		#write-host "MSOnline module not installed"
		try {
			Install-Module MSOnline -Confirm:$False -Force -SkipPublisherCheck -AllowClobber -WarningAction SilentlyContinue
			#write-host "MSOnline module now installed"
			$Actions += "- Installed the MSOnline Powershell Module"
            write-host "Installed the MSOnline Powershell Module" -ForegroundColor Yellow
            $ModuleModifiedCount += 1
	    }
		catch {
			$MajorErrors += "- Unable to Install the MSOnline Powershell Module"
            $Actions += "- Unable to Install the MSOnline Powershell Module"
            write-host "Unable to Install the MSOnline Powershell Module" -ForegroundColor Red
            if ($DetailedLoggingEnabled) {
                Add-Content -Path "$LogFile" -Value "Unable to Install the MSOnline Powershell Module" -Encoding Unicode
            }
		}
	} else {
        if ($monthlyupdatecheck -eq $true) {
            try {
                Update-Module MSOnline -Confirm:$False -Force -WarningAction SilentlyContinue
                $Actions += "- Updated the MSOnline Powershell Module"
                write-host "Updated the MSOnline Powershell Module" -ForegroundColor Yellow
                $ModuleModifiedCount += 1
                if ($DetailedLoggingEnabled) {
                    Add-Content -Path "$LogFile" -Value "Updated the MSOnline Powershell Module" -Encoding Unicode
                }
            }
            catch {
                $Actions += "- Unable to Update the MSOnline Powershell Module"
                $MajorErrors += "- Unable to Update the MSOnline Powershell Module"
                write-host "Unable to Update the MSOnline Powershell Module" -ForegroundColor Red
                if ($DetailedLoggingEnabled) {
                    Add-Content -Path "$LogFile" -Value "Unable to Update the MSOnline Powershell Module" -Encoding Unicode
                }
            }
        } else {
		    $Actions += "- Checked that MSOnline Powershell Module is present"
        }
	}
}



if ($MajorErrors.Count -eq 0 -and !($iniFile.PowerShell.DisableExchangeConnectorCheck -eq $true -or $iniFile.PowerShell.DisableExchangeConnectorCheck -eq 1)) {
	If ((Get-Module ExchangeOnlineManagement -ListAvailable) -eq $null){
		#write-host "ExchangeOnlineV2 module not installed"
		try {
			Install-Module ExchangeOnlineManagement -Confirm:$False -Force -SkipPublisherCheck -AllowClobber -WarningAction SilentlyContinue
			#write-host "MSOnline module now installed"
			$Actions += "- Installed the ExchangeOnline Powershell Module"
            write-host "Installed the ExchangeOnline Powershell Module" -ForegroundColor Yellow
            $ModuleModifiedCount += 1
            if ($DetailedLoggingEnabled) {
                Add-Content -Path "$LogFile" -Value "Installed the ExchangeOnlineV2 Powershell Module" -Encoding Unicode
            }
	    }
		catch {
			$MajorErrors += "- Unable to Install the ExchangeOnline Powershell Module"
            $Actions += "- Unable to Install the ExchangeOnline Powershell Module"
            write-host "Unable to Install the ExchangeOnline Powershell Module" -ForegroundColor Red
            if ($DetailedLoggingEnabled) {
                Add-Content -Path "$LogFile" -Value "Unable to Install the ExchangeOnline Powershell Module" -Encoding Unicode
            }
		}
	} else {
        if ($monthlyupdatecheck -eq $true) {
            try {
                Update-Module ExchangeOnlineManagement -Confirm:$False -Force -WarningAction SilentlyContinue
                $Actions += "- Updated the ExchangeOnline Powershell Module"
                write-host "Updated the ExchangeOnline Powershell Module" -ForegroundColor Yellow
                $ModuleModifiedCount += 1
                if ($DetailedLoggingEnabled) {
                    Add-Content -Path "$LogFile" -Value "Updated the ExchangeOnline Powershell Module" -Encoding Unicode
                }
            }
            catch {
                $Actions += "- Unable to Update the ExchangeOnline Powershell Module"
                $MajorErrors += "- Unable to Update the ExchangeOnline Powershell Module"
                write-host "Unable to Update the ExchangeOnline Powershell Module" -ForegroundColor Red
                if ($DetailedLoggingEnabled) {
                    Add-Content -Path "$LogFile" -Value "Unable to Update the ExchangeOnline Powershell Module" -Encoding Unicode
                }
            }
        } else {
		    $Actions += "- Checked that ExchangeOnline Powershell Module is present"
        }
	}
}

if ($MajorErrors.Count -eq 0 -and !($iniFile.PowerShell.DisableMicrosoftTeamsCheck -eq $true -or $iniFile.PowerShell.DisableMicrosoftTeamsCheck -eq 1)) {
	
    $packproviders = @(get-module -ListAvailable | where {($_.Name -eq "PowershellGet" -and $_.Version.Major[0] -gt 1 )})
    
    if ($packproviders.count -gt 0) {
        $teamsmodule = @(get-module -listavailable | where {($_.Name -eq "MicrosoftTeams") } | sort Version | select -Last 1)

        If ($teamsmodule.count -eq 0 -or ($teamsmodule.version.major -lt 2)) {
		    #write-host "MicrosoftTeams module not installed"
    # v 2.x.x is the new minimum require version
		    try {
			    Install-Module MicrosoftTeams -AllowPrerelease -Confirm:$False -Force -AllowClobber -SkipPublisherCheck -WarningAction SilentlyContinue
            # -AllowPrerelease only available with updated PowershellGet
			    #write-host "MicrosoftTeams module now installed"
			    $Actions += "- Installed the Microsoft Teams Powershell Module"
                write-host "Installed the Microsoft Teams Powershell Module" -ForegroundColor Yellow
                $ModuleModifiedCount += 1
			    }
		    catch {
			    $MajorErrors += "- Unable to Install the Microsoft Teams Powershell Module"
                $Actions += "- Unable to Install the Microsoft Teams Powershell Module"
                write-host "Unable to Install the Microsoft Teams Powershell Module" -ForegroundColor Red
                if ($DetailedLoggingEnabled) {
                    Add-Content -Path "$LogFile" -Value "Unable to Install the Microsoft Teams Powershell Module" -Encoding Unicode
                }
		    }
	    } else {
            if ($monthlyupdatecheck -eq $true) {
                try {
                    Update-Module MicrosoftTeams -AllowPrerelease -Confirm:$False -Force -WarningAction SilentlyContinue
                    $Actions += "- Updated the Microsoft Teams Powershell Module"
                    write-host "Updated the Microsoft Teams Powershell Module" -ForegroundColor Yellow
                    $ModuleModifiedCount += 1
                    if ($DetailedLoggingEnabled) {
                        Add-Content -Path "$LogFile" -Value "Updated the Microsoft Teams Powershell Module" -Encoding Unicode
                    }
                }
                catch {
                    $Actions += "- Unable to Update the Microsoft Teams Powershell Module"
                    $MajorErrors += "- Unable to Update the Microsoft Teams Powershell Module"
                    write-host "Unable to Update the Microsoft Teams Powershell Module" -ForegroundColor Red
                    if ($DetailedLoggingEnabled) {
                        Add-Content -Path "$LogFile" -Value "Unable to Update the Microsoft Teams Powershell Module" -Encoding Unicode
                    }
                    }
            } else {
		        $Actions += "- Checked that Microsoft Teams Powershell Module is present"
            }
	    }
    } else {
    	$MajorErrors += "- Unable to Install the Microsoft Teams Powershell Module as the required version of PowershellGet is not installed"
        $Actions += "- Unable to Install the Microsoft Teams Powershell Module as the required version of PowershellGet is not installed"
        write-host "Unable to Install the Microsoft Teams Powershell Module as the required version of PowershellGet is not installed" -ForegroundColor Red
        if ($DetailedLoggingEnabled) {
            Add-Content -Path "$LogFile" -Value "Unable to Install the Microsoft Teams Powershell Module as the required version of PowershellGet is not installed" -Encoding Unicode
        }
    }
}



if ($ModuleModifiedCount -gt 0) {
    $Actions += "- $ModuleModifiedCount Powershell modules were installed or updated"
    write-host "$ModuleModifiedCount Powershell modules were installed or updated" -ForegroundColor Yellow
}
if ($DetailedLoggingEnabled) {
	Add-Content -Path "$LogFile" -Value "$ModuleModifiedCount Powershell modules were installed or updated" -Encoding Unicode
}

if ($MajorErrors.Count -eq 0) {
    Set-RegistryValue -Path "$regalt\Global Information" -Name 'CurrentMonth' -ValueType DWORD -Value $now.month
}

#if ($MajorErrors.Count -eq 0 ) {
if ( $iniFile.Credentials.GA_Password -and $iniFile.Credentials.GA_Username) {
    $GAPassSec = ConvertTo-SecureString -AsPlainText $iniFile.Credentials.GA_Password -Force
    $GACreds = New-Object -TypeName System.Management.Automation.PSCredential -ArgumentList $iniFile.Credentials.GA_Username,$GAPassSec
    #$onMSEmail = $iniFile.Tenancy.OnMicrosoftCOMPrefix + ".onmicrosoft.com"
} else {
    $EmailAvailable = $false
}

if ($ApplytoAllTeachers -eq $true) {
	$OtherSettings += "- Override: Apply to All Teachers: $ApplytoAllTeachers (This affects setting Filtered Security Groups, Filtered Unified Groups, Filtered Distribution Groups, Teams Packages and Teams Policies)"
}
if ($ApplytoAllStudents -eq $true) {
	$OtherSettings += "- Override: Apply to All Students: $ApplytoAllStudents (This affects setting the Student Department, Filtered Security Groups, Filtered Unified Groups, Filtered Distribution Groups, Teams Packages and Teams Policies)"
}
$OtherSettings += "- Override: GlobalOnFailureApplytoAllUsersOnNextRun: $GlobalOnFailureApplytoAllUsersOnNextRun"


if ($SourceGroups) {
	$SourceGroupsCR = $SourceGroups | Out-String
} else {
	$SourceGroupsCR = "(no groups or not required)" | Out-String
}

if ($LicensingInformation) {
	$LicensingInformationCR = $LicensingInformation | Out-String
} else {
	$LicensingInformationCR = "(no information or not required)" | Out-String
}

if ($DetailedLoggingEnabled -eq $true -or $Verbose -eq $true) {
    $CredentialsCR = $Credentials | Out-String
    $TenancyCR = $Tenancy | Out-String


    if ($PoliciesToProcess) {
		$PoliciesToProcessCR = ( $PoliciesToProcess | select -Property * -ExcludeProperty CollectionIDsToExclude | ft * -AutoSize | Out-String -Width 4096)
	} else {
		$PoliciesToProcessCR = "(no policies)" | Out-String
	}
   
    if ($PackagesToProcess) {
		$PackagesToProcessCR = ( $PackagesToProcess | select -Property * -ExcludeProperty CollectionIDsToExclude | ft * -AutoSize | Out-String -Width 4096)
	} else {
		$PackagesToProcessCR = "(no packages)" | Out-String
	}
	
    if ($UnifiedGroups) {
		$UnifiedGroupsCR = $UnifiedGroups | Out-String
	} else {
		$UnifiedGroupsCR = "(no groups)" | Out-String
	}
	
    if ($FilteredSecurityGroupsToProcess) {
		$FilteredSecurityGroupsToProcessCR = ( $FilteredSecurityGroupsToProcess | select -Property * -ExcludeProperty CollectionIDsToExclude | ft * -AutoSize | Out-String -Width 4096 )    
	} else {
		$FilteredSecurityGroupsToProcessCR = "(no security groups)" | Out-String
	}
	
    if ($FilteredUnifiedGroupsToProcess) {
		$FilteredUnifiedGroupsToProcessCR = ( $FilteredUnifiedGroupsToProcess | select -Property * -ExcludeProperty CollectionIDsToExclude | ft * -AutoSize | Out-String -Width 4096 )
	} else {
		$FilteredUnifiedGroupsToProcessCR = "(no unified groups)" | Out-String
	}  
	
	if ($FilteredDistributionGroupsToProcess) {
		$FilteredDistributionGroupsToProcessCR = ( $FilteredDistributionGroupsToProcess | select -Property * -ExcludeProperty CollectionIDsToExclude | ft * -AutoSize | Out-String -Width 4096 )
	} else {
		$FilteredDistributionGroupsToProcessCR = "(no distribution groups)" | Out-String
	}
    
	if ($Department) {
		$DepartmentCR = $Department | Out-String
	} else {
		$DepartmentCR = "(none)" | Out-String
	}
    $EmailInfoCR = $EmailInfo | Out-String
    $OtherSettingsCR = $OtherSettings | Out-String
}

if ($Verbose -eq $true) {    
    Write-EventLog –LogName Application –Source $AppLogName –EntryType Information –EventID ($AppLogBaseIndex+1) -Category $AppLogCategory -Message ("$Caption - $ProgramName Version: $Version - Settings Processed`r`nChannel / PatchLevel: $Channel / $PatchLevel`r`nPID: $Pid`r`n`r`nProgram Location: $Myname`r`nSettings File: $SettingsFileName`r`nProfile Name: $ProfileName`r`n`r`nCredentials:`r`n$CredentialsCR`r`nM365:`r`n$TenancyCR`r`nSource Groups:`r`n$SourceGroupsCR`r`nLicensing Information in Use:`r`n$LicensingInformationCR`r`nAll Teachers / All Students Unified Groups:`r`n$UnifiedGroupsCR`r`nDepartment:`r`n$DepartmentCR`r`nFiltered Security Groups: $($FilteredSecurityGroupsToProcess.count)`r`nFiltered Unified Groups: $($FilteredUnifiedGroupsToProcess.count)`r`nFiltered Distribution Groups: $($FilteredDistributionGroupsToProcess.count)`r`nFiltered Packages:`r`n$($PackagesToProcess.count)`r`nFiltered Policies:`r`n$($PoliciesToProcess.count)`r`nResults Emails:`r`n$EmailInfoCR`r`nOther Settings:`r`n$OtherSettingsCR")
}


$ConnectedtoMSOLService = $false
$ConnectedtoMicrosoftExchange = $false
$ConnectedtoAzureAD = $false
$ConnectedtoTeams = $false

if ($MajorErrors.Count -eq 0) {

    if ($DetailedLoggingEnabled) {
	    Add-Content -Path "$LogFile" -Value "`r`nBeginning Processing" -Encoding Unicode
        Add-Content -Path "$LogFile" -Value "====================`r`n" -Encoding Unicode
    }
}


# Start - Connect to MSOnline
if ($MajorErrors.Count -eq 0 -and $NeedsMSONline -eq $true) {
    write-host "Connecting to MSOnline" -foregroundcolor green
    Connect-MsolService -Credential $GACreds
    $e = $error.count
	$MSOLService = get-msoldomain
	
	if ( !($MSOLService)) {
		$errorstring = ""
		if ($error.count -gt $e) {
			if ($Error[0].Exception.Message) {
				$errorstring = $Error[0].Exception.Message
			} else {
				$errorstring = "(unknown)"
			}
		} else {
			$errorstring = "(unknown)"	
		}
		
        $MajorErrors += "- Could not connect to the MSOLService. Check the credentials and version of the MSOnline Powershell Module. Error message: $errorstring"
        write-host "Could not connect to the MSOLService. Check the credentials and version of the MSOnline Powershell Module. Error message: $errorstring" -foreground red
        if ($DetailedLoggingEnabled) {
            Add-Content -Path "$LogFile" -Value "Could not connect to the MSOLService. Check the credentials and version of the MSOnline Powershell Module. Error message: $errorstring" -Encoding Unicode
        }
    } else {
        $ConnectedtoMSOLService = $true
        $Actions += "- Connected to MSOnline: Domain Count: $($MSOLService.Count)"
        if ($DetailedLoggingEnabled) {
            $MSOLService | select -Property * -ExcludeProperty Extensiondata | sort @{expression='IsDefault';Descending=$true},@{expression="Name";Ascending=$true} | ft * -AutoSize | Out-String -Width 4096 | Out-File -filepath "$LogFile" -append -Encoding Unicode
            Add-Content -Path "$LogFile" -Value "Connected to MSOnline: Domain Count: $($MSOLService.Count)`r`n" -Encoding Unicode
        }
    }
}



function Get-MSOLRecursive {
param ($ObjectID, $GroupsToIgnore)

    if ($GroupstoIgnore -eq $Null) { $GroupsToIgnore = @() }
    $GroupsToIgnore += $ObjectID
    $UserMembers = @()
    $GroupsMembers = @()

    $UserMembers = @(Get-MsolGroupMember -GroupObjectId $ObjectId -MemberObjectTypes User -All)
    # Needs to be array in case there is just one member

    $GroupsMembers = @(Get-MsolGroupMember -GroupObjectId $ObjectId -MemberObjectTypes Group -All)

    if ( $GroupsMembers.count -gt 0) {
        foreach ($GroupObj in $GroupsMembers) {
            if ($GroupsToIgnore -notcontains $GroupObj.ObjectID) {
                $UserMembers += Get-MSOLRecursive -ObjectID $GroupObj.ObjectID -GroupsToIgnore $GroupsToIgnore
            }
        }
    }

    #$UserMembers = @($UserMembers | Sort-Object -Property EmailAddress -Unique) # 1.0.61
	$UserMembers = @($UserMembers | Sort-Object -Property ObjectID -Unique) # 1.0.87
    Return $UserMembers
}

#Example: $TeacherList=Get-MSOLRecursive -ObjectID $TeacherSourceGroup.objectid


function Get-MSOLGroupRecursive {
#this function is not currently used 1.0.97
param ($ObjectID, $IncludeDistributionGroups, $GroupsToIgnore)

    $Groups = @()
    if ($GroupstoIgnore -eq $Null) { $GroupsToIgnore = @() }

    $Groups = @(Get-MSOLGroup -ObjectId $objectID)
    if ($IncludeDistributionGroups -eq $false) {
        if ($Groups[0].GroupType -eq 'DistributionList') {
            Return @()
        }
    }
    $GroupsToIgnore += $ObjectID

    $Groupstosearch = @(Get-MsolGroupMember -GroupObjectId $ObjectId -MemberObjectTypes Group -All)

    if ( $Groupstosearch.count -gt 0) {
        foreach ($GroupObj in $Groupstosearch) {
            if ($GroupsToIgnore -notcontains $GroupObj.ObjectID) {        
                $Groups += Get-MSOLGroupRecursive -ObjectID $GroupObj.ObjectID -IncludeDistributionGroups $IncludeDistributionGroups -GroupsToIgnore $GroupsToIgnore
            }
        }
    }

    $Groups = @($Groups | Sort-Object -Property ObjectId -Unique) # 1.0.71

    Return $Groups
}
#Example: $TeacherList=Get-MSOLGroupRecursive -ObjectID $TeacherSourceGroup.objectid -IncludeDistributionGroups $IncludeDistributionGroups

function BuildGroupTable {
param ($GroupInformation)

    $GroupRulesOK=$true

    $RequiredRecursiveIDs =@() # user must be a member of at least one of each of the RequiredRecursiveIDs.NestedGroups. RequiredRecursiveIDs.NestedGroups also contains what would have been $RequiredIDs
	$RequiredORRecursiveIDs =@() # user must be a member of at least one group in the list
    $NotRequiredRecursiveIDs =@() # user must not be a member of any of each of the NotRequiredRecursiveIDs.NestedGroups. NotRequiredRecursiveIDs.NestedGroups also contains what would have been $NotRequiredIDs

    $ProblemGroups =@()

    [int]$RequiredSets = 0
	[int]$RequiredORSets = 0
    [int]$NotRequiredSets = 0

    if ($GroupInformation.RequiredGroups -ne $null) {
        if ($GroupInformation.RequiredGroups[0].count -gt 0) { # in case empty
            foreach ($GroupI in $GroupInformation.RequiredGroups) {
                $searchresults = @()
                if ((Test-IsGuid -ObjectGuid $GroupI) -eq $true) {
                    $searchresults =  @(get-msolgroup -Objectid $GroupI -ErrorAction SilentlyContinue)
                } else {
                    $searchresults =  @(get-msolgroup -SearchString $GroupI -ErrorAction SilentlyContinue)
                    if ($GroupInformation.DisableNameorIDCheckingForGroupRules -eq $false) {
                        $searchresults = @($searchresults | where { $_.CommonName -like $GroupI -or $_.DisplayName -like $GroupI -or $_.EmailAddress -like $GroupI }) # 1.0.21
                    }
                    if ($GroupInformation.IncludeDistributionGroups -eq $false) {
                        $searchresults = @($searchresults | where { $_.GroupType -ne 'DistributionList' }) # 1.0.21
                    }
                }

                if ($searchresults.count -eq 0) {
                    $ProblemGroups += [PSCustomObject]@{
                        'GroupName' = $GroupI
						'IncludeDistributionGroups' = $GroupInformation.IncludeDistributionGroups
						'DisableNameorIDCheckingForGroupRules' = $GroupInformation.DisableNameorIDCheckingForGroupRules
                        'IsGUID' =  (Test-IsGuid -ObjectGuid $GroupI)
                        'Recursive' = $false
                        'Setting' = 'RequiredGroups'
                        'Problem' = 'Missing'
                        'GroupRuleImpossible' = $true
                    }
                    $GroupRulesOK=$false
                }

                if ($searchresults.count -gt 0) {
                    foreach ($groupresult in $searchresults) {
                        $RequiredSets += 1
                        $RequiredRecursiveIDs += [PSCustomObject]@{
                            'Source' = 'RequiredGroups'
                            'ObjectID' = $groupresult.objectid
                            'DisplayName' = $groupresult.DisplayName
                            'CommonName' = $groupresult.CommonName
                            'Description' = $groupresult.description
                            'GroupType' = $groupresult.GroupType
                            'EmailAddress' = $groupresult.EmailAddress
                            'NestedGroups' = $groupresult.objectid
                            'NestedDisplayNames' = if ($groupresult.DisplayName -eq $null -or $groupresult.DisplayName -eq "") { "-" } else { $groupresult.DisplayName }
                            'NestedCommonNames' = if ($groupresult.CommonName -eq $null -or $groupresult.CommonName -eq "") { "-" } else { $groupresult.CommonName }
                            'NestedDescriptions' = if ($groupresult.Description -eq $null -or $groupresult.Description -eq "") { "-" } else { $groupresult.Description }
                            'NestedGroupTypes' = $groupresult.GroupType
							'NestedEmailAddresses' = if ($groupresult.EmailAddress -eq $null -or $groupresult.EmailAddress -eq "") { "-" } else { $groupresult.EmailAddress }
                        }
                    }
                }
            }
        }
    }

   
   	    if ($GroupInformation.RequiredOrGroups -ne $null) {
        if ($GroupInformation.RequiredOrGroups[0].count -gt 0) { # in case empty
            foreach ($GroupI in $GroupInformation.RequiredOrGroups) {
                $searchresults = @()
                if ((Test-IsGuid -ObjectGuid $GroupI) -eq $true) {
                    $searchresults =  @(get-msolgroup -Objectid $GroupI -ErrorAction SilentlyContinue)
                } else {
                    $searchresults =  @(get-msolgroup -SearchString $GroupI -ErrorAction SilentlyContinue)
                    if ($GroupInformation.DisableNameorIDCheckingForGroupRules -eq $false) {
                        $searchresults = @($searchresults | where { $_.CommonName -like $GroupI -or $_.DisplayName -like $GroupI -or $_.EmailAddress -like $GroupI }) # 1.0.21
                    }
                    if ($GroupInformation.IncludeDistributionGroups -eq $false) {
                        $searchresults = @($searchresults | where { $_.GroupType -ne 'DistributionList' }) # 1.0.21
                    }
                }

                if ($searchresults.count -eq 0) {
                    $ProblemGroups += [PSCustomObject]@{
                        'GroupName' = $GroupI
						'IncludeDistributionGroups' = $GroupInformation.IncludeDistributionGroups
						'DisableNameorIDCheckingForGroupRules' = $GroupInformation.DisableNameorIDCheckingForGroupRules
                        'IsGUID' =  (Test-IsGuid -ObjectGuid $GroupI)
                        'Recursive' = $false
                        'Setting' = 'RequiredOrGroups'
                        'Problem' = 'Missing'
                        'GroupRuleImpossible' = $true
                    }
                    $GroupRulesOK=$false
                }

                if ($searchresults.count -gt 0) {
                    foreach ($groupresult in $searchresults) {
                        $RequiredORSets += 1
                        $RequiredORRecursiveIDs += [PSCustomObject]@{
                            'Source' = 'RequiredOrGroups'
                            'ObjectID' = $groupresult.objectid
                            'DisplayName' = $groupresult.DisplayName
                            'CommonName' = $groupresult.CommonName
                            'Description' = $groupresult.description
                            'GroupType' = $groupresult.GroupType
                            'EmailAddress' = $groupresult.EmailAddress
                            'NestedGroups' = $groupresult.objectid
							'NestedDisplayNames' = if ($groupresult.DisplayName -eq $null -or $groupresult.DisplayName -eq "") { "-" } else { $groupresult.DisplayName }
                            'NestedCommonNames' = if ($groupresult.CommonName -eq $null -or $groupresult.CommonName -eq "") { "-" } else { $groupresult.CommonName }
                            'NestedDescriptions' = if ($groupresult.Description -eq $null -or $groupresult.Description -eq "") { "-" } else { $groupresult.Description }
							'NestedGroupTypes' = $groupresult.GroupType
                            'NestedEmailAddresses' = if ($groupresult.EmailAddress -eq $null -or $groupresult.EmailAddress -eq "") { "-" } else { $groupresult.EmailAddress }
                        }
                    }
                }
            }
        }
    }
   

    if ($GroupInformation.RequiredRecursiveGroups -ne $null) {
        if ($GroupInformation.RequiredRecursiveGroups[0].count -gt 0) { # in case empty
            foreach ($GroupI in $GroupInformation.RequiredRecursiveGroups) {
                $searchresults = @()
                if ((Test-IsGuid -ObjectGuid $GroupI) -eq $true) {
                    $searchresults =  @(get-msolgroup -Objectid $GroupI -ErrorAction SilentlyContinue)
                } else {
                    $searchresults =  @(get-msolgroup -SearchString $GroupI -ErrorAction SilentlyContinue)
                    if ($GroupInformation.DisableNameorIDCheckingForGroupRules -eq $false) {
                        $searchresults = $searchresults | where { $_.CommonName -like $GroupI -or $_.DisplayName -like $GroupI -or $_.EmailAddress -like $GroupI } # 1.0.21
                    }
                    if ($GroupInformation.IncludeDistributionGroups -eq $false) {
                        $searchresults = $searchresults | where { $_.GroupType -ne 'DistributionList' } # 1.0.21
                    }
                }

                if ($searchresults.count -eq 0) {
                    $ProblemGroups += [PSCustomObject]@{
                        'GroupName' = $GroupI
						'IncludeDistributionGroups' = $GroupInformation.IncludeDistributionGroups
						'DisableNameorIDCheckingForGroupRules' = $GroupInformation.DisableNameorIDCheckingForGroupRules
                        'IsGUID' =  (Test-IsGuid -ObjectGuid $GroupI)
                        'Recursive' = $true
                        'Setting' = 'RequiredRecursiveGroups'
                        'Problem' = 'Missing'
                        'GroupRuleImpossible' = $true
                    }
                    $GroupRulesOK=$false
                }
                if ($searchresults.count -gt 0) {
                    foreach ($groupresult in $searchresults) {
                        $recursivegroups = Get-MSOLGroupRecursive -ObjectID $groupresult.objectid -IncludeDistributionGroups $GroupInformation.IncludeDistributionGroups
                        $RequiredSets += 1
                        $RequiredRecursiveIDs += [PSCustomObject]@{
                            'Source' = 'RequiredRecursiveGroups'
                            'ObjectID' = $groupresult.objectid
                            'DisplayName' = $groupresult.DisplayName
                            'CommonName' = $groupresult.CommonName
                            'Description' = $groupresult.description
                            'GroupType' = $groupresult.GroupType
                            'EmailAddress' = $groupresult.EmailAddress
                            'NestedGroups' = @($recursivegroups.objectID)
                            'NestedDisplayNames' = @($recursivegroups.DisplayName | %{ if ($_ -eq $null -or $_ -eq "") { "-" } else { $_ }})
                            'NestedCommonNames' = @($recursivegroups.CommonName | %{ if ($_ -eq $null -or $_ -eq "") { "-" } else { $_ }})
                            'NestedDescriptions' = @($recursivegroups.Description | %{ if ($_ -eq $null -or $_ -eq "") { "-" } else { $_ }})
							'NestedGroupTypes' = @($recursivegroups.GroupType)
                            'NestedEmailAddresses' = @($recursivegroups.EmailAddress | %{ if ($_ -eq $null -or $_ -eq "") { "-" } else { $_ }})
                        }
                    }
                }
            }
        }
    }
   
       if ($GroupInformation.RequiredOrRecursiveGroups -ne $null) {
        if ($GroupInformation.RequiredOrRecursiveGroups[0].count -gt 0) { # in case empty
            foreach ($GroupI in $GroupInformation.RequiredOrRecursiveGroups) {
                $searchresults = @()
                if ((Test-IsGuid -ObjectGuid $GroupI) -eq $true) {
                    $searchresults =  @(get-msolgroup -Objectid $GroupI -ErrorAction SilentlyContinue)
                } else {
                    $searchresults =  @(get-msolgroup -SearchString $GroupI -ErrorAction SilentlyContinue)
                    if ($GroupInformation.DisableNameorIDCheckingForGroupRules -eq $false) {
                        $searchresults = $searchresults | where { $_.CommonName -like $GroupI -or $_.DisplayName -like $GroupI -or $_.EmailAddress -like $GroupI } # 1.0.21
                    }
                    if ($GroupInformation.IncludeDistributionGroups -eq $false) {
                        $searchresults = $searchresults | where { $_.GroupType -ne 'DistributionList' } # 1.0.21
                    }
                }

                if ($searchresults.count -eq 0) {
                    $ProblemGroups += [PSCustomObject]@{
                        'GroupName' = $GroupI
						'IncludeDistributionGroups' = $GroupInformation.IncludeDistributionGroups
						'DisableNameorIDCheckingForGroupRules' = $GroupInformation.DisableNameorIDCheckingForGroupRules
                        'IsGUID' =  (Test-IsGuid -ObjectGuid $GroupI)
                        'Recursive' = $true
                        'Setting' = 'RequiredOrRecursiveGroups'
                        'Problem' = 'Missing'
                        'GroupRuleImpossible' = $true
                    }
                    $GroupRulesOK=$false
                }
                if ($searchresults.count -gt 0) {
                    foreach ($groupresult in $searchresults) {
                        $recursivegroups = Get-MSOLGroupRecursive -ObjectID $groupresult.objectid -IncludeDistributionGroups $GroupInformation.IncludeDistributionGroups
                        $RequiredORSets += 1
                        $RequiredORRecursiveIDs += [PSCustomObject]@{
                            'Source' = 'RequiredOrRecursiveGroups'
                            'ObjectID' = $groupresult.objectid
                            'DisplayName' = $groupresult.DisplayName
                            'CommonName' = $groupresult.CommonName
                            'Description' = $groupresult.description
                            'GroupType' = $groupresult.GroupType
                            'EmailAddress' = $groupresult.EmailAddress
                            'NestedGroups' = @($recursivegroups.objectID)
                            'NestedDisplayNames' = @($recursivegroups.DisplayName | %{ if ($_ -eq $null -or $_ -eq "") { "-" } else { $_ }})
                            'NestedCommonNames' = @($recursivegroups.CommonName | %{ if ($_ -eq $null -or $_ -eq "") { "-" } else { $_ }})
                            'NestedDescriptions' = @($recursivegroups.Description | %{ if ($_ -eq $null -or $_ -eq "") { "-" } else { $_ }})
							'NestedGroupTypes' = @($recursivegroups.GroupType)
                            'NestedEmailAddresses' = @($recursivegroups.EmailAddress | %{ if ($_ -eq $null -or $_ -eq "") { "-" } else { $_ }})
                        }
                    }
                }
            }
        }
    }   
   


    if ($GroupInformation.NotRequiredGroups -ne $null) {
        if ($GroupInformation.NotRequiredGroups[0].count -gt 0) { # in case empty
            foreach ($GroupI in $GroupInformation.NotRequiredGroups) {
                $searchresults = @()
                if ((Test-IsGuid -ObjectGuid $GroupI) -eq $true) {
                    $searchresults =  @(get-msolgroup -Objectid $GroupI -ErrorAction SilentlyContinue)
                } else {
                    $searchresults =  @(get-msolgroup -SearchString $GroupI -ErrorAction SilentlyContinue)
                    if ($GroupInformation.DisableNameorIDCheckingForGroupRules -eq $false) {
                        $searchresults = $searchresults | where { $_.CommonName -like $GroupI -or $_.DisplayName -like $GroupI -or $_.EmailAddress -like $GroupI } # 1.0.21
                    }
                    if ($GroupInformation.IncludeDistributionGroups -eq $false) {
                        $searchresults = $searchresults | where { $_.GroupType -ne 'DistributionList' } # 1.0.21
                    }
                }

                if ($searchresults.count -eq 0) {
                    $ProblemGroups += [PSCustomObject]@{
                        'GroupName' = $GroupI
						'IncludeDistributionGroups' = $GroupInformation.IncludeDistributionGroups
						'DisableNameorIDCheckingForGroupRules' = $GroupInformation.DisableNameorIDCheckingForGroupRules
                        'IsGUID' =  (Test-IsGuid -ObjectGuid $GroupI)
                        'Recursive' = $false
                        'Setting' = 'NotRequiredGroups'
                        'Problem' = 'Missing'
                        'GroupRuleImpossible' = $true

                    }
                    if ($GroupInformation.DontAllowMissingNotRequiredGroups -eq $true) {
                        $GroupRulesOK=$false
                    }
                }
                if ($searchresults.count -gt 0) {
                    foreach ($groupresult in $searchresults) {
                        $NotRequiredSets += 1
                        $NotRequiredRecursiveIDs += [PSCustomObject]@{
                            'Source' = 'NotRequiredGroups'
                            'ObjectID' = $groupresult.objectid
                            'DisplayName' = $groupresult.DisplayName
                            'CommonName' = $groupresult.CommonName
                            'Description' = $groupresult.description
                            'GroupType' = $groupresult.GroupType
                            'EmailAddress' = $groupresult.EmailAddress
                            'NestedGroups' = $groupresult.objectid
                            'NestedDisplayNames' = if ($groupresult.DisplayName -eq $null -or $groupresult.DisplayName -eq "") { "-" } else { $groupresult.DisplayName }
                            'NestedCommonNames' = if ($groupresult.CommonName -eq $null -or $groupresult.CommonName -eq "") { "-" } else { $groupresult.CommonName }
                            'NestedDescriptions' = if ($groupresult.Description -eq $null -or $groupresult.Description -eq "") { "-" } else { $groupresult.Description }
							'NestedGroupTypes' = $groupresult.GroupType
                            'NestedEmailAddresses' = if ($groupresult.EmailAddress -eq $null -or $groupresult.EmailAddress -eq "") { "-" } else { $groupresult.EmailAddress }
                        }
                    }
                }
            }
        }
    }



    if ($GroupInformation.NotRequiredRecursiveGroups -ne $null) {
        if ($GroupInformation.NotRequiredRecursiveGroups[0].count -gt 0) { # in case empty
            foreach ($GroupI in $GroupInformation.NotRequiredRecursiveGroups) {
                $searchresults = @()
                if ((Test-IsGuid -ObjectGuid $GroupI) -eq $true) {
                    $searchresults =  @(get-msolgroup -Objectid $GroupI -ErrorAction SilentlyContinue)
                } else {
                    $searchresults =  @(get-msolgroup -SearchString $GroupI -ErrorAction SilentlyContinue)
                    if ($GroupInformation.DisableNameorIDCheckingForGroupRules -eq $false) {
                        $searchresults = $searchresults | where { $_.CommonName -like $GroupI -or $_.DisplayName -like $GroupI -or $_.EmailAddress -like $GroupI }
                    }
                    if ($GroupInformation.IncludeDistributionGroups -eq $false) {
                        $searchresults = $searchresults | where { $_.GroupType -ne 'DistributionList' } # 1.0.21
                    }
                }

                if ($searchresults.count -eq 0) {
                    $ProblemGroups += [PSCustomObject]@{
                        'GroupName' = $GroupI
						'IncludeDistributionGroups' = $GroupInformation.IncludeDistributionGroups
						'DisableNameorIDCheckingForGroupRules' = $GroupInformation.DisableNameorIDCheckingForGroupRules
                        'IsGUID' =  (Test-IsGuid -ObjectGuid $GroupI)
                        'Recursive' = $false
                        'Setting' = 'NotRequiredRecursiveGroups'
                        'Problem' = 'Missing'
                        'GroupRuleImpossible' = $true

                    }
                    if ($GroupInformation.DontAllowMissingNotRequiredGroups -eq $true) {
                        $GroupRulesOK=$false
                    }
                }
                if ($searchresults.count -gt 0) {
                    foreach ($groupresult in $searchresults) {
                        $recursivegroups = Get-MSOLGroupRecursive -ObjectID $groupresult.objectid -IncludeDistributionGroups $GroupInformation.IncludeDistributionGroups
                        $NotRequiredSets += 1
                        $NotRequiredRecursiveIDs += [PSCustomObject]@{
                            'Source' = 'NotRequiredRecursiveGroups'
                            'ObjectID' = $groupresult.objectid
                            'DisplayName' = $groupresult.DisplayName
                            'CommonName' = $groupresult.CommonName
                            'Description' = $groupresult.description
                            'GroupType' = $groupresult.GroupType
                            'EmailAddress' = $groupresult.EmailAddress
                            'NestedGroups' = @($recursivegroups.objectID)
                            'NestedDisplayNames' = @($recursivegroups.DisplayName | %{ if ($_ -eq $null -or $_ -eq "") { "-" } else { $_ }})
                            'NestedCommonNames' = @($recursivegroups.CommonName | %{ if ($_ -eq $null -or $_ -eq "") { "-" } else { $_ }})
                            'NestedDescriptions' = @($recursivegroups.Description | %{ if ($_ -eq $null -or $_ -eq "") { "-" } else { $_ }})
							'NestedGroupTypes' = @($recursivegroups.GroupType)
                            'NestedEmailAddresses' = @($recursivegroups.EmailAddress | %{ if ($_ -eq $null -or $_ -eq "") { "-" } else { $_ }})
                        }
                    }
                }
            }
        }
    }

# saftey net - these shouldnt be empty at this stage. Report a Rules Fault if they are
if ($RequiredRecursiveIDs -eq $null -and $NotRequiredRecursiveIDs -eq $null -and $RequiredORRecursiveIDs -eq $null) {
    $GroupRulesOK=$false
}
if ($RequiredRecursiveIDs.count -eq 0 -and $NotRequiredRecursiveIDs.count -eq 0 -and $RequiredORRecursiveIDs.count -eq 0) {
    $GroupRulesOK=$false
}

if ($RequiredSets -ne $RequiredRecursiveIDs.count) { # 1.0.24 safety net
    $GroupRulesOK=$false

    $ProblemGroups += [PSCustomObject]@{
        'GroupName' = ""
		'IncludeDistributionGroups' = ""
		'DisableNameorIDCheckingForGroupRules' = ""
        'IsGUID' =  ""
        'Recursive' = ""
        'Setting' = 'Required'
        'Problem' = 'Processing Error'
        'GroupRuleImpossible' = ""
    }
}

if ($RequiredORSets -ne $RequiredORRecursiveIDs.count) { # 1.0.24 safety net
    $GroupRulesOK=$false
    $ProblemGroups += [PSCustomObject]@{
        'GroupName' = ""
		'IncludeDistributionGroups' = ""
		'DisableNameorIDCheckingForGroupRules' = ""
        'IsGUID' =  ""
        'Recursive' = ""
        'Setting' = 'RequiredOR'
        'Problem' = 'Processing Error'
        'GroupRuleImpossible' = ""
    }
}


if ($NotRequiredSets -ne $NotRequiredRecursiveIDs.count) { # 1.0.24 safety net
    $GroupRulesOK=$false
    $ProblemGroups += [PSCustomObject]@{
        'GroupName' = ""
		'IncludeDistributionGroups' = ""
		'DisableNameorIDCheckingForGroupRules' = ""
        'IsGUID' =  ""
        'Recursive' = ""
        'Setting' = 'NotRequired'
        'Problem' = 'Processing Error'
        'GroupRuleImpossible' = ""
    }
}


return $GroupRulesOK, $ProblemGroups, $RequiredRecursiveIDs, $RequiredORRecursiveIDs, $NotRequiredRecursiveIDs
} #end function


function GetGroupRuleOLUsers {
# Takes the Group Rules and returns a list of the users that meets the rules. Uses Get-MsolGroupMember and is for Source Group Processing

param ($RequiredRecursiveIDs, $RequiredORRecursiveIDs, $NotRequiredRecursiveIDs)

$GroupMatchedUsers = @()
if ($RequiredORRecursiveIDs.count -gt 0 -or $RequiredRecursiveIDs.count -gt 0) {
    if ($RequiredORRecursiveIDs.count -gt 0) {
        foreach ($group in @($RequiredORRecursiveIDs.NestedGroups.Guid)) {
            $Processusers = $null
            $Processusers = @(Get-MsolGroupMember -GroupObjectId $Group -All -MemberObjectTypes User)
            $GroupMatchedUsers += @($Processusers | where {$_.ObjectID -notin $GroupMatchedUsers.ObjectID})
        }
    }
    $SkipFirst = $false
    if ($RequiredORRecursiveIDs.count -eq 0) {
		$GroupMatchedUsers += @(Get-MsolGroupMember -GroupObjectId $RequiredRecursiveIDs[0].NestedGroups.Guid -All -MemberObjectTypes User)
        $SkipFirst = $true
    }
    $count = 0
    if ($RequiredRecursiveIDs.count -gt 0) {
        foreach ($group in @($RequiredRecursiveIDs.NestedGroups.Guid)) {
            if ($count -gt 0 -or $SkipFirst -eq $false) {
                $Processusers = $null
				$Processusers = @(Get-MsolGroupMember -GroupObjectId $Group -All -MemberObjectTypes User)
                $GroupMatchedUsers = @($GroupMatchedUsers | where {$_.ObjectID -in $Processusers.ObjectID})
            }
            $Count++
        }
    }
} else {
    $GroupMatchedUsers= $null
	$TempUser = @(get-msoluser -All)
	$GroupMatchedUsers = @($TempUser | Select @{ Label = "CommonName" ; Expression = { ("") } }, @{ Label = "DisplayName" ; Expression = { ($_.DisplayName) } }, @{ Label = "EmailAddress" ; Expression = { ($_.SignInName) } }, @{ Label = "GroupMemberType" ; Expression = { ("Member") } }, @{ Label = "IsLicensed" ; Expression = { ($_.IsLicensed) } }, @{ Label = "LastDirSyncTime" ; Expression = { ($_.LastDirSyncTime) } }, @{ Label = "ObjectID" ; Expression = { ($_.ObjectID) } }, @{ Label = "OverallProvisioningStatus" ; Expression = { ($_.OverallProvisioningStatus) } }, @{ Label = "ValidationStatus" ; Expression = { ($_.ValidationStatus) } })
}

if ($NotRequiredRecursiveIDs.count -gt 0) {
    foreach ($group in @($NotRequiredRecursiveIDs.NestedGroups.Guid)) {
        # $GroupMatchedUsers = @($GroupMatchedUsers | where $_.ObjectID -notin @((Get-AzureADGroup -ObjectId $Group.ObjectID | Get-AzureADGroupMember -All $true).ObectID)) # do not add missing squiggly brackets as this causes performance issues
        $Processusers = $null
		$Processusers = @(Get-MsolGroupMember -GroupObjectId $Group -All -MemberObjectTypes User)
		
		
        $GroupMatchedUsers = @($GroupMatchedUsers | where {$_.ObjectID -notin $Processusers.ObjectID})
    }
}
$GroupMatchedUsers = @($GroupMatchedUsers | Sort EmailAddress)
return ($GroupMatchedUsers)
}



function GetGroupRuleUsers {
# Takes the Group Rules and returns a list of the users that meets the rules. Uses Get-AzureADGroupMember and is for all Group Processing except Source Groups

param ($RequiredRecursiveIDs, $RequiredORRecursiveIDs, $NotRequiredRecursiveIDs)

$GroupMatchedUsers = @()
if ($RequiredORRecursiveIDs.count -gt 0 -or $RequiredRecursiveIDs.count -gt 0) {
    if ($RequiredORRecursiveIDs.count -gt 0) {
        foreach ($group in @($RequiredORRecursiveIDs.NestedGroups.Guid)) {
            #$GroupMatchedUsers += (@(Get-AzureADGroup -ObjectId $Group.ObjectID | Get-AzureADGroupMember -All $true) | where { $GroupMatchedUsers.ObjectID -notcontains $_.ObjectID} )
            $Processusers = $null
            $Processusers = @(Get-AzureADGroup -ObjectId $Group | Get-AzureADGroupMember -All $true)
            $GroupMatchedUsers += @($Processusers | where {$_.ObjectID -notin $GroupMatchedUsers.ObjectID})
        }
    }
    $SkipFirst = $false
    if ($RequiredORRecursiveIDs.count -eq 0) {
        $GroupMatchedUsers += @(Get-AzureADGroup -ObjectId $RequiredRecursiveIDs[0].NestedGroups.Guid | Get-AzureADGroupMember -All $true)
        $SkipFirst = $true
    }
    $count = 0
    if ($RequiredRecursiveIDs.count -gt 0) {
        foreach ($group in @($RequiredRecursiveIDs.NestedGroups.Guid)) {
            if ($count -gt 0 -or $SkipFirst -eq $false) {
                # $GroupMatchedUsers = @($GroupMatchedUsers | where $_.ObjectID -in @((Get-AzureADGroup -ObjectId $Group.ObjectID | Get-AzureADGroupMember -All $true).ObjectID)) # do not add missing squiggly brackets as this causes performance issues
                $Processusers = $null
                $Processusers = @(Get-AzureADGroup -ObjectId $Group | Get-AzureADGroupMember -All $true)
                $GroupMatchedUsers = @($GroupMatchedUsers | where {$_.ObjectID -in $Processusers.ObjectID})
            }
            $Count++
        }
    }
} else {
    $GroupMatchedUsers= $null
    $GroupMatchedUsers = @(AzureADPreview\Get-AzureADUser -All $true -ErrorAction SilentlyContinue -WarningAction SilentlyContinue)
}

if ($NotRequiredRecursiveIDs.count -gt 0) {
    foreach ($group in @($NotRequiredRecursiveIDs.NestedGroups.Guid)) {
        # $GroupMatchedUsers = @($GroupMatchedUsers | where $_.ObjectID -notin @((Get-AzureADGroup -ObjectId $Group.ObjectID | Get-AzureADGroupMember -All $true).ObectID)) # do not add missing squiggly brackets as this causes performance issues
        $Processusers = $null
        $Processusers = @(Get-AzureADGroup -ObjectId $Group | Get-AzureADGroupMember -All $true)
        $GroupMatchedUsers = @($GroupMatchedUsers | where {$_.ObjectID -notin $Processusers.ObjectID})
    }
}
$GroupMatchedUsers = $GroupMatchedUsers | where { $_.ObjectType -eq 'User' }
return ($GroupMatchedUsers)
}


Function Get-CN {
param (
	[parameter(Mandatory=$true)]
	[ValidateNotNullOrEmpty()]$DN
)

	$SplitOU = if ($DN -match ",") { @(iex "echo $($DN.Trim($TrimChar))" ) } else { @($DN) }
	$Sections = $SplitOU -split ","
	$CNString = ""

	foreach ($section in $sections) {
		if ($section -like ("DC=*")) {
			$CNString= $CNString + $section.TrimStart("DC").TrimStart("=") + "."
		}
	}

	$CNString=$CNString.Trimend(".") +"/"


	For ($a=$sections.count; $a-- ; $a -gt -1) {
		if ($sections[$a] -like ("OU=*")) {
			$CNString= $CNString + $sections[$a].TrimStart("OU").TrimStart("=") + "/"
	   }
	}

	#$CNString=$CNString.Trimend("/")

	Return $CNString
}



Function Check_ADCanonicalName {
param (
    $UserCN,
    $RequiredOrOUs,
    $NotRequiredOUs
)
    $found = $false

    if ($UserCN) {
        #if ($UserCN -notcontains '/') { $UserCN = Get-CN -DN $UserCN }
        if ($UserCN.Split('/').Count -lt 2) { $UserCN = Get-CN -DN $UserCN }
    }

    if ($UserCN) { 
        foreach ($PotentialCN in $RequiredOrOUs) {
            if ($UserCN -like $PotentialCN) { $found = $true }
        }

        if ($found -eq $true) {
            foreach ($PotentialNotCN in $NotRequiredOUs) {
                if ($UserCN -like $PotentialNotCN) { $found = $false }
            }
        }
    }
    return ($found)
}


function Get-TimeElapsed {

param (
[parameter(Mandatory=$true)]
[ValidateNotNullOrEmpty()]$StartTime,


[parameter(Mandatory=$true)]
[ValidateNotNullOrEmpty()]$Processed,

[parameter(Mandatory=$true)]
[ValidateNotNullOrEmpty()]$ToProcess)

$UsersProcessedString = ""
$timestring = ""
$timeremainingseconds=0

#            $UsersProcessedString, $timestring, $timeremainingseconds = get-timeelapsed -StartTime $funcstarttime -Processed $UserProcessed -ToProcess ($UsersToProcess.Count)

    $endtime = (Get-Date)
    $SecondsSoFar=[int](New-TimeSpan -Start $StartTime -End $endtime).TotalSeconds
    $timeremainingseconds=0
    $timeremainingseconds = (($SecondsSoFar / $Processed) * $ToProcess) - $SecondsSoFar
    $copytimeremainingseconds = $timeremainingseconds
    #$timeremainingseconds = [math]::round($timeremainingseconds,1)

    $timestring = ""

    if ($timeremainingseconds -gt 0) {
		if ($timeremainingseconds -ge 86400) {
			$days = [math]::floor($timeremainingseconds/86400)
			$timeremainingseconds = $timeremainingseconds - ($days * 86400)

			if ($days -ne 1) {
				if ($days -lt 10) {
					$timestring = "  $days days"
				} else {
					$timestring = " $days days"
				}
			} else {
				$timestring = "  $days day "
			}
		}		
					
        if ($timeremainingseconds -ge 3600) {
            $hours = [math]::floor($timeremainingseconds/3600)
            $timeremainingseconds = $timeremainingseconds - ($hours * 3600)

			if ($hours -ne 1) {
				if ($hours -lt 10) {
					$timestring = "$timestring  $hours hrs"	
				} else {
					$timestring = "$timestring $hours hrs"	
				}
			} else {
				$timestring = "$timestring  $hours hr "
			}
        }

        $minutes = [math]::floor($timeremainingseconds/60)
        $timeremainingseconds = $timeremainingseconds - ($minutes * 60)
        if ($minutes -ne 1) {
			if ($minutes -lt 10) {
				$timestring = "$timestring  $minutes mins"	
			} else {
				$timestring = "$timestring $minutes mins"
			}
        } else {
            $timestring = "$timestring  $minutes min "
        }

        $timeremainingseconds = [math]::floor($timeremainingseconds)
        if ($timeremainingseconds -ne 1) {
			if ($timeremainingseconds -lt 10) {
				$timestring = "$timestring  $timeremainingseconds secs"
			} else {
				$timestring = "$timestring $timeremainingseconds secs"
			}
        } else {
            $timestring = "$timestring  $timeremainingseconds sec "
        }

	$timestring=$timestring+ " (" + $endtime.AddSeconds($copytimeremainingseconds).ToString("dd/MM/yyyy HH:mm:ss") + ")"

    } else {
        $timestring = " 0 secs "
    }

	$UsersProcessedString = [string]$Processed
	$UsersProcessedString = $UsersProcessedString.PadLeft(($ToProcess -as [string]).length," ")

return $UsersProcessedString, $timestring, $copytimeremainingseconds

} # end function

Function Process-UserCSVOutput {

[CmdletBinding()]

Param(
    #[Parameter(ValueFromPipelineByPropertyName)]
    [Parameter(ValueFromPipeline=$true)]
    $CSVData
)

Begin {}

Process {
    if ($DetailedLoggingEnabled) {
		$global:FormatEnumerationLimit = -1
        if ($CSVData) {
            if ($UseCSVforUserDataInLogFile -eq $false) { # 1.0.58
			#if ($UseCSVforUserDataInLogFile -eq $false -and $Verbose -eq $false ) {
                $CSVData | ft * -AutoSize | Out-String -Width 4096 | Out-File -filepath "$LogFile" -append -Encoding Unicode
            } else {
                $CSVStringData = @($CSVData | ConvertTo-CSV)
                if ($CSVStringData.Count -gt 0) {
                    $null, $CSVStringData = $CSVStringData
                    Add-Content -Path "$LogFile" -Value "" -Encoding Unicode
                    $CSVStringData | Out-String -Width 4096 | Out-File -filepath "$LogFile" -append -Encoding Unicode
                    Add-Content -Path "$LogFile" -Value "" -Encoding Unicode
                    $CSVStringData = $null
                }
            }
        }
    }
}
End {}

} # end function

# GlobalAdminTeamCreation

if ($MajorErrors.Count -eq 0 -and ( $iniFile.GlobalAdminTeamsCreation.AddGlobalAdminToCreateTeamsGroup -eq $true -or $iniFile.GlobalAdminTeamsCreation.AddGlobalAdminToCreateTeamsGroup -eq 1)) {
    write-host "Global Admin Teams Creation Group Membership" -foregroundcolor Green
    if ($DetailedLoggingEnabled) {
        Add-Content -Path "$LogFile" -Value "`r`nGlobal Admin Teams Creation Group Membership" -Encoding Unicode
        Add-Content -Path "$LogFile" -Value "============================================`r`n" -Encoding Unicode
        Add-Content -Path "$LogFile" -Value "Search Group Name: $($iniFile.GlobalAdminTeamsCreation.CreateTeamsGroupName)`r`n" -Encoding Unicode
    }
    $TeacherTeamCreationGroup = $null
    #$TeacherTeamCreationGroup = @(Get-MsolGroup -SearchString $iniFile.GlobalAdminTeamsCreation.CreateTeamsGroupName)

	if ((Test-IsGuid -ObjectGuid $iniFile.GlobalAdminTeamsCreation.CreateTeamsGroupName) -eq $false) {
        $TeacherTeamCreationGroup = @(Get-MsolGroup -SearchString $iniFile.GlobalAdminTeamsCreation.CreateTeamsGroupName | where {$_.GroupType -eq 'Security' -or $_.GroupType -eq 'MailEnabledSecurity'}) # 1.0.21
        
        if ($iniFile.GlobalAdminTeamsCreation.DisableNameOrIDChecking -ne $true -and $iniFile.GlobalAdminTeamsCreation.DisableNameOrIDChecking -ne 1) {
	        $TeacherTeamCreationGroup = $TeacherTeamCreationGroup | where { $_.CommonName -like $iniFile.GlobalAdminTeamsCreation.CreateTeamsGroupName -or $_.DisplayName -like $iniFile.GlobalAdminTeamsCreation.CreateTeamsGroupName -or $_.EmailAddress -like $iniFile.GlobalAdminTeamsCreation.CreateTeamsGroupName } # 1.0.22
        }
    } else {
        $TeacherTeamCreationGroup = @(Get-MsolGroup -ObjectID $iniFile.GlobalAdminTeamsCreation.CreateTeamsGroupName)
    }
    
    if ($TeacherTeamCreationGroup.Count -eq 1) {
		if ($DetailedLoggingEnabled) {
            Add-Content -Path "$LogFile" -Value "Resulting Group DisplayName: $($TeacherTeamCreationGroup.DisplayName)" -Encoding Unicode
			Add-Content -Path "$LogFile" -Value "Resulting Group ObjectID: $($TeacherTeamCreationGroup.objectid)" -Encoding Unicode
        }
        $TeamCreators = $null
        $TeamCreators = @(Get-MsolGroupMember -GroupObjectId $TeacherTeamCreationGroup.objectid -All -MemberObjectTypes User)

        if ($TeamCreators.EmailAddress -notcontains $iniFile.Credentials.GA_Username) {
            if ($MakeChanges -eq $true) {
                write-host "Adding $($iniFile.Credentials.GA_Username) to the Teams Creation Security Group $($iniFile.GlobalAdminTeamsCreation.CreateTeamsGroupName)" -foreground Yellow
                $Actions += "- Adding $($iniFile.Credentials.GA_Username) to the Teams Creation Security Group $($iniFile.GlobalAdminTeamsCreation.CreateTeamsGroupName)"
                if ($DetailedLoggingEnabled) {
                    Add-Content -Path "$LogFile" -Value "Adding $($iniFile.Credentials.GA_Username) to the Teams Creation Security Group $($iniFile.GlobalAdminTeamsCreation.CreateTeamsGroupName)" -Encoding Unicode
                }
                $e = $error.count
                Add-MsolGroupMember -GroupObjectId $TeacherTeamCreationGroup.objectid -GroupMemberType User -GroupMemberObjectId (Get-MsolUser -UserPrincipalName $iniFile.Credentials.GA_Username).objectId -ErrorAction SilentlyContinue
                if ($error.count -gt $e) {
                	$errorstring = ""
					if ($Error[0].Exception.Message) {
						$errorstring = $Error[0].Exception.Message
					} else {
						$errorstring = "(unknown)"
					}
                    write-host "Could not add $($iniFile.Credentials.GA_Username) to the Teams Creation Security Group $($iniFile.GlobalAdminTeamsCreation.CreateTeamsGroupName) - error message: $errorstring" -foreground red
                    if ($DetailedLoggingEnabled) {
                        Add-Content -Path "$LogFile" -Value "Could not add $($iniFile.Credentials.GA_Username) to the Teams Creation Security Group $($iniFile.GlobalAdminTeamsCreation.CreateTeamsGroupName) - error message: $errorstring" -Encoding Unicode
                    }
                    $Warnings += "- Could not add $($iniFile.Credentials.GA_Username) to the Teams Creation Security Group $($iniFile.GlobalAdminTeamsCreation.CreateTeamsGroupName) - error message: $errorstring"
                }
            } else {
                write-host "Simulation: Adding $($iniFile.Credentials.GA_Username) to the Teams Creation Security Group $($iniFile.GlobalAdminTeamsCreation.CreateTeamsGroupName)" -foreground cyan
                $Actions += "- Simulation: Adding $($iniFile.Credentials.GA_Username) to the Teams Creation Security Group $($iniFile.GlobalAdminTeamsCreation.CreateTeamsGroupName)"
                if ($DetailedLoggingEnabled) {
                    Add-Content -Path "$LogFile" -Value "Simulation: Adding $($iniFile.Credentials.GA_Username) to the Teams Creation Security Group $($iniFile.GlobalAdminTeamsCreation.CreateTeamsGroupName)" -Encoding Unicode
                }
            }
        } else {
            write-host "$($iniFile.Credentials.GA_Username) is already a member of the Teams Creation Security Group $($iniFile.GlobalAdminTeamsCreation.CreateTeamsGroupName)" -foreground green
            $Actions += "- $($iniFile.Credentials.GA_Username) is already a member of the Teams Creation Security Group $($iniFile.GlobalAdminTeamsCreation.CreateTeamsGroupName)"
            if ($DetailedLoggingEnabled) {
                Add-Content -Path "$LogFile" -Value "$($iniFile.Credentials.GA_Username) is already a member of the Teams Creation Security Group $($iniFile.GlobalAdminTeamsCreation.CreateTeamsGroupName)" -Encoding Unicode
            }
        }
    } else {
        if ($TeacherTeamCreationGroup.Count -gt 1) {
            $Warnings += "- More than one group matches the name $($iniFile.GlobalAdminTeamsCreation.CreateTeamsGroupName), please check the name. The Global Admin Team Creation Group will not be processed"
            write-host "More than one group matches the name $($iniFile.GlobalAdminTeamsCreation.CreateTeamsGroupName), please check the name. The Global Admin Team Creation Group will not be processed" -foreground red
            if ($DetailedLoggingEnabled) {
                Add-Content -Path "$LogFile" -Value "More than one group matches the name $($iniFile.GlobalAdminTeamsCreation.CreateTeamsGroupName), please check the name. The Global Admin Team Creation Group will not be processed" -Encoding Unicode
            }
        } else {
            $Warnings += "- The Teams Creation Group $($iniFile.GlobalAdminTeamsCreation.CreateTeamsGroupName) does not exist. The Global Admin Team Creation Group will not be processed"
            write-host "The Teams Creation Group $($iniFile.GlobalAdminTeamsCreation.CreateTeamsGroupName) does not exist. The Global Admin Team Creation Group will not be processed" -foreground red
            if ($DetailedLoggingEnabled) {
                Add-Content -Path "$LogFile" -Value "The Teams Creation Group $($iniFile.GlobalAdminTeamsCreation.CreateTeamsGroupName) does not exist. The Global Admin Team Creation Group will not be processed" -Encoding Unicode
            }
        }
    }

    #Add-MsolGroupMember -GroupObjectId (Get-MsolGroup -SearchString $TeamCreate).objectid -GroupMemberType User -GroupMemberObjectId (Get-MsolUser -UserPrincipalName $GAUser).objectId

}

# Get List of Teachers
$TeacherList = @()
$count = $null
$TeacherSourceGroup = $null
$TeacherSourceGroup = ""
$TeacherSourceGroupCount = $null
if ($MajorErrors.Count -eq 0 -and $NeedsTeacherList -eq $true ) {

	$GroupRules = @()
    $GroupRules += [PSCustomObject]@{
	    'HasGroupRules' = $iniFile.TeacherSourceGroups.HasGroups
	    'RequiredGroups' = $iniFile.TeacherSourceGroups.RequiredGroups
	    'RequiredRecursiveGroups' = $iniFile.TeacherSourceGroups.RequiredRecursiveGroups
	    'RequiredOrGroups' = $iniFile.TeacherSourceGroups.RequiredOrGroups
	    'RequiredOrRecursiveGroups' = $iniFile.TeacherSourceGroups.RequiredOrRecursiveGroups
	    'NotRequiredGroups' = $iniFile.TeacherSourceGroups.NotRequiredGroups
	    'NotRequiredRecursiveGroups' = $iniFile.TeacherSourceGroups.NotRequiredRecursiveGroups
        'DisableNameorIDCheckingForGroupRules' = $iniFile.TeacherSourceGroups.DisableNameorIDCheckingForGroupRules
        'DontAllowMissingNotRequiredGroups' = $iniFile.TeacherSourceGroups.DontAllowMissingNotRequiredGroups
        'IncludeDistributionGroups' = $iniFile.TeacherSourceGroups.IncludeDistributionGroupsInGroupRules
    }
	if ($GroupRules.RequiredGroups) {
		$TeacherSourceGroup += "Required Groups: "
		foreach ($Group in $GroupRules.RequiredGroups) { 
			$TeacherSourceGroup += "$Group "
		}
	}
	if ($GroupRules.RequiredRecursiveGroups) {
		if ($TeacherSourceGroup -ne "") { $TeacherSourceGroup = "$TeacherSourceGroup / " }
		$TeacherSourceGroup += "Required Recursive Groups: "
		foreach ($Group in $GroupRules.RequiredRecursiveGroups) { 
			$TeacherSourceGroup += "$Group "
		}
	}
	if ($GroupRules.RequiredOrGroups) {
		if ($TeacherSourceGroup -ne "") { $TeacherSourceGroup = "$TeacherSourceGroup / " }
		$TeacherSourceGroup += "Required OR Groups: "
		foreach ($Group in $GroupRules.RequiredOrGroups) { 
			$TeacherSourceGroup += "$Group "
		}
	}
	if ($GroupRules.RequiredOrRecursiveGroups) {
		if ($TeacherSourceGroup -ne "") { $TeacherSourceGroup = "$TeacherSourceGroup / " }
		$TeacherSourceGroup += "Required OR Recursive Groups: "
		foreach ($Group in $GroupRules.RequiredOrRecursiveGroups) { 
			$TeacherSourceGroup += "$Group "
		}
	}
	if ($GroupRules.NotRequiredGroups) {
		if ($TeacherSourceGroup -ne "") { $TeacherSourceGroup = "$TeacherSourceGroup / " }
		$TeacherSourceGroup += "Not Required Groups: "
		foreach ($Group in $GroupRules.NotRequiredGroups) { 
			$TeacherSourceGroup += "$Group "
		}
	}
	if ($GroupRules.NotRequiredRecursiveGroups) {
		if ($TeacherSourceGroup -ne "") { $TeacherSourceGroup = "$TeacherSourceGroup / " }
		$TeacherSourceGroup += "Not Required Recursive Groups: "
		foreach ($Group in $GroupRules.NotRequiredRecursiveGroups) { 
			$TeacherSourceGroup += "$Group "
		}
	}	
	$TeacherSourceGroupCount = @($GroupRules.RequiredGroups).count + @($GroupRules.RequiredRecursiveGroups).count + @($GroupRules.RequiredOrGroups).count + @($GroupRules.RequiredOrRecursiveGroups).count + @($GroupRules.NotRequiredGroups).count + @($GroupRules.NotRequiredRecursiveGroups).count 

	write-host "Teacher Source Groups - Groups to Process: $TeacherSourceGroup" -ForegroundColor green
	write-host "Teacher Source Groups: Building Group Rules Table" -ForegroundColor green
	
	if ($DetailedLoggingEnabled) {
		Add-Content -Path "$LogFile" -Value "`r`nGetting the Source List of Teachers: Building Group Rules Table" -Encoding Unicode
		Add-Content -Path "$LogFile" -Value "===============================================================`r`n" -Encoding Unicode
		Add-Content -Path "$LogFile" -Value "`r`nGroup Rules Settings"
		Add-Content -Path "$LogFile" -Value  "--------------------"
		$GroupRules | ft * -AutoSize | Out-String -Width 4096 | Out-File -filepath "$LogFile" -append -Encoding Unicode
	}
	$Actions += "- Teacher Source Groups - Groups to Process: $TeacherSourceGroup"
	$e = $error.count
	$GroupRulesOK, $ProblemGroups, $RequiredRecursiveIDs, $RequiredORRecursiveIDs, $NotRequiredRecursiveIDs = BuildGroupTable -GroupInformation $GroupRules
	if ($GroupRulesOK -eq $true) {
		write-host "Group Rules OK: $GroupRulesOK   Problem Groups: $($ProblemGroups.count)   RequiredGroups: $($RequiredRecursiveIDs.count)   RequiredOrGroups: $($RequiredORRecursiveIDs.count)   NotRequiredGroups: $($NotRequiredRecursiveIDs.count)" -ForegroundColor green
		$TeacherList = GetGroupRuleOLUsers -RequiredRecursiveIDs $RequiredRecursiveIDs -RequiredORRecursiveIDs $RequiredORRecursiveIDs -NotRequiredRecursiveIDs $NotRequiredRecursiveIDs
		
		write-host "Found $($TeacherList.count) users matching the Teacher Source Group Rules" -ForegroundColor green
	} else {
		write-host "Group Rules OK: $GroupRulesOK   Problem Groups: $($ProblemGroups.count)   RequiredGroups: $($RequiredRecursiveIDs.count)   RequiredOrGroups: $($RequiredORRecursiveIDs.count)   NotRequiredGroups: $($NotRequiredRecursiveIDs.count)" -ForegroundColor yellow
		$warnings += "- Teacher Source Groups: Group Rules OK: $GroupRulesOK   Problem Groups: $($ProblemGroups.count)   RequiredGroups: $($RequiredRecursiveIDs.count)   RequiredOrGroups: $($RequiredORRecursiveIDs.count)   NotRequiredGroups: $($NotRequiredRecursiveIDs.count)"
		$Warnings += "- There was a problem with Teacher Source Group Processing. Teachers will not be processed"		
	}

	if ($error.count -gt $e) {
		$errorstring = ""
		if ($Error[0].Exception.Message) {
			$errorstring = $Error[0].Exception.Message
		} else {
			$errorstring = "(unknown)"
		}
		write-host "Teacher Source Groups: There was an error in processing Group Rules or matching users - error message: $errorstring" -foreground red
		if ($DetailedLoggingEnabled) {
			Add-Content -Path "$LogFile" -Value "Teacher Source Groups There was an error in processing Group Rules or matching users - error message: $errorstring" -Encoding Unicode
		}
		$Warnings += "- Teacher Source Groups: There was an error in processing Group Rules or matching users - error message: $errorstring"
		$GotGroupUsersOK = $false
	}

	if ($DetailedLoggingEnabled) {
		if ($RequiredRecursiveIDs.count -gt 0) {
			Add-Content -Path "$LogFile" -Value "Teacher Source Group Rules: RequiredGroups, Including Recursive"
			Add-Content -Path "$LogFile" -Value "---------------------------------------------------------------"
			$RequiredRecursiveIDs | Sort DisplayName,  CommonName, Description, ObjectID | fl * | Out-String -Width 4096 | Out-File -filepath "$LogFile" -append -Encoding Unicode
		}
		if ($RequiredORRecursiveIDs.count -gt 0) {
			Add-Content -Path "$LogFile" -Value "Teacher Source Group Rules: RequiredOrGroups, Including Recursive"
			Add-Content -Path "$LogFile" -Value "-----------------------------------------------------------------"
			$RequiredORRecursiveIDs | Sort DisplayName,  CommonName, Description, ObjectID | fl * | Out-String -Width 4096 | Out-File -filepath "$LogFile" -append -Encoding Unicode
		}					
		if ($NotRequiredRecursiveIDs.count -gt 0) {
			Add-Content -Path "$LogFile" -Value "Teacher Source Group Rules: NotRequiredGroups, Including Recursive"
			Add-Content -Path "$LogFile" -Value "------------------------------------------------------------------"
			$NotRequiredRecursiveIDs | Sort DisplayName,  CommonName, Description, ObjectID | fl * | Out-String -Width 4096 | Out-File -filepath "$LogFile" -append -Encoding Unicode
		}	
		if ($ProblemGroups.count -gt 0) {
			Add-Content -Path "$LogFile" -Value "Teacher Source Group Rules: Problem Groups"
			Add-Content -Path "$LogFile" -Value "------------------------------------------"
			$ProblemGroups | Sort DisplayName,  CommonName, Description, ObjectID | fl * | Out-String -Width 4096 | Out-File -filepath "$LogFile" -append -Encoding Unicode
		}
		Add-Content -Path "$LogFile" -Value "Teacher Source Groups: Group Rules OK: $GroupRulesOK   Problem Groups: $($ProblemGroups.count)   RequiredGroups: $($RequiredRecursiveIDs.count)   RequiredOrGroups: $($RequiredORRecursiveIDs.count)   NotRequiredGroups: $($NotRequiredRecursiveIDs.count)" -Encoding Unicode
		Add-Content -Path "$LogFile" -Value "Teacher Source Groups: Found $($TeacherList.count) users matching the Group Rules" -Encoding Unicode
	}
	$Actions += "- Teacher Source Groups: Found $($TeacherList.count) users matching the Group Rules"

# IsLicensed = $false is the same as AzureAD user {$_.Assignedlicenses.count -eq 0}

	if ($TeacherList.Count -eq 0 -and $GroupRulesOK -eq $true) {
		$Warnings += "- There are no Teachers available from the Teacher Source Groups. Teachers will not be processed"
		write-host "There are no Teachers available from the Teacher Source Groups. Teachers will not be processed" -foregroundcolor yellow
		if ($DetailedLoggingEnabled) {
			Add-Content -Path "$LogFile" -Value "There are no Teachers available from the Teacher Source Groups. Teachers will not be processed" -Encoding Unicode
		}
	}

	if ($TeacherList.Count -gt 0) {
		if ($iniFile.TeacherSourceGroups.DomainFilter ) {
			[int]$count = $TeacherList.Count
			$TeacherList = @($TeacherList | where { $iniFile.TeacherSourceGroups.DomainFilter -contains $_.EmailAddress.Split('@')[1] }) #1.0.83
			[int]$count = $count - $TeacherList.Count
			write-host "Filtering source Teachers against $($iniFile.TeacherSourceGroups.DomainFilter.Count) Email Domain filters: $($iniFile.TeacherSourceGroups.DomainFilter)" -foreground green
			write-host "$count Teachers Removed from Processing by Teachers Email Domain Filters" -foregroundcolor $alttextcolour
			if ($DetailedLoggingEnabled) {
				Add-Content -Path "$LogFile" -Value "Filtering source Teachers against $($iniFile.TeacherSourceGroups.DomainFilter.Count) Email Domain filters: $($iniFile.TeacherSourceGroups.DomainFilter)" -Encoding Unicode
				Add-Content -Path "$LogFile" -Value "$count Teachers Removed from Processing by Teachers Email Domain Filters" -Encoding Unicode
			}
			$Actions += "- $($TeacherList.Count) Filtered Source Group Teachers to be processed that have one of these Email Domains: $($iniFile.TeacherSourceGroups.DomainFilter)"
			if ($TeacherList.Count -eq 0) {
				$Warnings += "- After filtering, there are no Teachers available from the Teacher Source Groups. Check the Teacher Email Domain filter is correct - $($iniFile.TeacherSourceGroups.DomainFilter)"
				write-host "After filtering, there are no Teachers available from the Teacher Source Groups. Check the Teacher Email Domain filter is correct - $($iniFile.TeacherSourceGroups.DomainFilter)" -foreground red
				if ($DetailedLoggingEnabled) {
					Add-Content -Path "$LogFile" -Value "After filtering, there are no Teachers available from the Teacher Source Groups. Check the Teacher Email Domain filter is correct - $($iniFile.TeacherSourceGroups.DomainFilter)" -Encoding Unicode
				}
			} 
		} 
	}
} else {
	if ($NeedsTeacherList -eq $false) {
		$Actions += "- Teacher Source Group Processing: Not required"
		write-host "Teacher Source Group Processing: Not required" -foreground yellow
		if ($DetailedLoggingEnabled) {
			Add-Content -Path "$LogFile" -Value "`r`nGetting the Source List of Teachers" -Encoding Unicode
			Add-Content -Path "$LogFile" -Value "===================================`r`n" -Encoding Unicode
			Add-Content -Path "$LogFile" -Value "`r`nGroup Rules Settings"
			Add-Content -Path "$LogFile" -Value  "--------------------"
			$GroupRules | ft * -AutoSize | Out-String -Width 4096 | Out-File -filepath "$LogFile" -append -Encoding Unicode			
			Add-Content -Path "$LogFile" -Value "`r`nTeacher Source Group Processing: Not required" -Encoding Unicode			
			
		}
	}
}

# Get List of Students
$StudentList = @()
$count = $null
$StudentSourceGroup = $null
$StudentSourceGroup = ""
$StudentSourceGroupCount = $null
if ($MajorErrors.Count -eq 0 -and $NeedsStudentList -eq $true ) {

	$GroupRules = @()
    $GroupRules += [PSCustomObject]@{
	    'HasGroupRules' = $iniFile.StudentSourceGroups.HasGroups
	    'RequiredGroups' = $iniFile.StudentSourceGroups.RequiredGroups
	    'RequiredRecursiveGroups' = $iniFile.StudentSourceGroups.RequiredRecursiveGroups
	    'RequiredOrGroups' = $iniFile.StudentSourceGroups.RequiredOrGroups
	    'RequiredOrRecursiveGroups' = $iniFile.StudentSourceGroups.RequiredOrRecursiveGroups
	    'NotRequiredGroups' = $iniFile.StudentSourceGroups.NotRequiredGroups
	    'NotRequiredRecursiveGroups' = $iniFile.StudentSourceGroups.NotRequiredRecursiveGroups
        'DisableNameorIDCheckingForGroupRules' = $iniFile.StudentSourceGroups.DisableNameorIDCheckingForGroupRules
        'DontAllowMissingNotRequiredGroups' = $iniFile.StudentSourceGroups.DontAllowMissingNotRequiredGroups
        'IncludeDistributionGroups' = $iniFile.StudentSourceGroups.IncludeDistributionGroupsInGroupRules
    }
	if ($GroupRules.RequiredGroups) {
		$StudentSourceGroup += "Required Groups: "
		foreach ($Group in $GroupRules.RequiredGroups) { 
			$StudentSourceGroup += "$Group "
		}
	}
	if ($GroupRules.RequiredRecursiveGroups) {
		if ($StudentSourceGroup -ne "") { $StudentSourceGroup = "$StudentSourceGroup / " }
		$StudentSourceGroup += "Required Recursive Groups: "
		foreach ($Group in $GroupRules.RequiredRecursiveGroups) { 
			$StudentSourceGroup += "$Group "
		}
	}
	if ($GroupRules.RequiredOrGroups) {
		if ($StudentSourceGroup -ne "") { $StudentSourceGroup = "$StudentSourceGroup / " }
		$StudentSourceGroup += "Required OR Groups: "
		foreach ($Group in $GroupRules.RequiredOrGroups) { 
			$StudentSourceGroup += "$Group "
		}
	}
	if ($GroupRules.RequiredOrRecursiveGroups) {
		if ($StudentSourceGroup -ne "") { $StudentSourceGroup = "$StudentSourceGroup / " }
		$StudentSourceGroup += "Required OR Recursive Groups: "
		foreach ($Group in $GroupRules.RequiredOrRecursiveGroups) { 
			$StudentSourceGroup += "$Group "
		}
	}
	if ($GroupRules.NotRequiredGroups) {
		if ($StudentSourceGroup -ne "") { $StudentSourceGroup = "$StudentSourceGroup / " }
		$StudentSourceGroup += "Not Required Groups: "
		foreach ($Group in $GroupRules.NotRequiredGroups) { 
			$StudentSourceGroup += "$Group "
		}
	}
	if ($GroupRules.NotRequiredRecursiveGroups) {
		if ($StudentSourceGroup -ne "") { $StudentSourceGroup = "$StudentSourceGroup / " }
		$StudentSourceGroup += "Not Required Recursive Groups: "
		foreach ($Group in $GroupRules.NotRequiredRecursiveGroups) { 
			$StudentSourceGroup += "$Group "
		}
	}
	$StudentSourceGroupCount = @($GroupRules.RequiredGroups).count + @($GroupRules.RequiredRecursiveGroups).count + @($GroupRules.RequiredOrGroups).count + @($GroupRules.RequiredOrRecursiveGroups).count + @($GroupRules.NotRequiredGroups).count + @($GroupRules.NotRequiredRecursiveGroups).count 	
	
	write-host "Student Source Groups - Groups to Process: $StudentSourceGroup" -ForegroundColor green
	write-host "Student Source Groups: Building Group Rules Table" -ForegroundColor green
	
	if ($DetailedLoggingEnabled) {
		Add-Content -Path "$LogFile" -Value "`r`nGetting the Source List of Students: Building Group Rules Table" -Encoding Unicode
		Add-Content -Path "$LogFile" -Value "===============================================================`r`n" -Encoding Unicode
		Add-Content -Path "$LogFile" -Value "`r`nGroup Rules Settings"
		Add-Content -Path "$LogFile" -Value  "--------------------"
		$GroupRules | ft * -AutoSize | Out-String -Width 4096 | Out-File -filepath "$LogFile" -append -Encoding Unicode
	}
	$Actions += "- Student Source Groups - Groups to Process: $StudentSourceGroup"
	$e = $error.count
	$GroupRulesOK, $ProblemGroups, $RequiredRecursiveIDs, $RequiredORRecursiveIDs, $NotRequiredRecursiveIDs = BuildGroupTable -GroupInformation $GroupRules
	if ($GroupRulesOK -eq $true) {
		write-host "Group Rules OK: $GroupRulesOK   Problem Groups: $($ProblemGroups.count)   RequiredGroups: $($RequiredRecursiveIDs.count)   RequiredOrGroups: $($RequiredORRecursiveIDs.count)   NotRequiredGroups: $($NotRequiredRecursiveIDs.count)" -ForegroundColor green
		$StudentList = GetGroupRuleOLUsers -RequiredRecursiveIDs $RequiredRecursiveIDs -RequiredORRecursiveIDs $RequiredORRecursiveIDs -NotRequiredRecursiveIDs $NotRequiredRecursiveIDs
		
		write-host "Found $($StudentList.count) users matching the Student Source Group Rules" -ForegroundColor green
	} else {
		write-host "Group Rules OK: $GroupRulesOK   Problem Groups: $($ProblemGroups.count)   RequiredGroups: $($RequiredRecursiveIDs.count)   RequiredOrGroups: $($RequiredORRecursiveIDs.count)   NotRequiredGroups: $($NotRequiredRecursiveIDs.count)" -ForegroundColor yellow
		$warnings += "- Student Source Groups: Group Rules OK: $GroupRulesOK   Problem Groups: $($ProblemGroups.count)   RequiredGroups: $($RequiredRecursiveIDs.count)   RequiredOrGroups: $($RequiredORRecursiveIDs.count)   NotRequiredGroups: $($NotRequiredRecursiveIDs.count)"
		$Warnings += "- There was a problem with Student Source Group Processing. Student will not be processed"
	}

	if ($error.count -gt $e) {
		$errorstring = ""
		if ($Error[0].Exception.Message) {
			$errorstring = $Error[0].Exception.Message
		} else {
			$errorstring = "(unknown)"
		}
		write-host "Student Source Groups: There was an error in processing Group Rules or matching users - error message: $errorstring" -foreground red
		if ($DetailedLoggingEnabled) {
			Add-Content -Path "$LogFile" -Value "Student Source Groups There was an error in processing Group Rules or matching users - error message: $errorstring" -Encoding Unicode
		}
		$Warnings += "- Student Source Groups: There was an error in processing Group Rules or matching users - error message: $errorstring"
		$GotGroupUsersOK = $false
	}

	if ($DetailedLoggingEnabled) {
		if ($RequiredRecursiveIDs.count -gt 0) {
			Add-Content -Path "$LogFile" -Value "Student Source Group Rules: RequiredGroups, Including Recursive"
			Add-Content -Path "$LogFile" -Value "---------------------------------------------------------------"
			$RequiredRecursiveIDs | Sort DisplayName,  CommonName, Description, ObjectID | fl * | Out-String -Width 4096 | Out-File -filepath "$LogFile" -append -Encoding Unicode
		}
		if ($RequiredORRecursiveIDs.count -gt 0) {
			Add-Content -Path "$LogFile" -Value "Student Source Group Rules: RequiredOrGroups, Including Recursive"
			Add-Content -Path "$LogFile" -Value "-----------------------------------------------------------------"
			$RequiredORRecursiveIDs | Sort DisplayName,  CommonName, Description, ObjectID | fl * | Out-String -Width 4096 | Out-File -filepath "$LogFile" -append -Encoding Unicode
		}					
		if ($NotRequiredRecursiveIDs.count -gt 0) {
			Add-Content -Path "$LogFile" -Value "Student Source Group Rules: NotRequiredGroups, Including Recursive"
			Add-Content -Path "$LogFile" -Value "------------------------------------------------------------------"
			$NotRequiredRecursiveIDs | Sort DisplayName,  CommonName, Description, ObjectID | fl * | Out-String -Width 4096 | Out-File -filepath "$LogFile" -append -Encoding Unicode
		}
		if ($ProblemGroups.count -gt 0) {
			Add-Content -Path "$LogFile" -Value "Student Source Group Rules: Problem Groups"
			Add-Content -Path "$LogFile" -Value "------------------------------------------"
			$ProblemGroups | Sort DisplayName,  CommonName, Description, ObjectID | fl * | Out-String -Width 4096 | Out-File -filepath "$LogFile" -append -Encoding Unicode
		}
		Add-Content -Path "$LogFile" -Value "Student Source Groups: Group Rules OK: $GroupRulesOK   Problem Groups: $($ProblemGroups.count)   RequiredGroups: $($RequiredRecursiveIDs.count)   RequiredOrGroups: $($RequiredORRecursiveIDs.count)   NotRequiredGroups: $($NotRequiredRecursiveIDs.count)" -Encoding Unicode
		Add-Content -Path "$LogFile" -Value "Student Source Groups: Found $($StudentList.count) users matching the Group Rules" -Encoding Unicode
	}
	$Actions += "- Student Source Groups: Found $($StudentList.count) users matching the Group Rules"

# IsLicensed = $false is the same as AzureAD user {$_.Assignedlicenses.count -eq 0}

	if ($StudentList.Count -eq 0 -and $GroupRulesOK -eq $true) {
		$Warnings += "- There are no Students available from the Student Source Groups. Students will not be processed"
		write-host "There are no Students available from the Student Source Groups. Students will not be processed" -foregroundcolor yellow
		if ($DetailedLoggingEnabled) {
			Add-Content -Path "$LogFile" -Value "There are no Students available from the Student Source Groups. Students will not be processed" -Encoding Unicode
		}
	}

	if ($StudentList.Count -gt 0) {
		if ($iniFile.StudentSourceGroups.DomainFilter ) {
			[int]$count = $StudentList.Count
			$StudentList = @($StudentList | where { $iniFile.StudentSourceGroups.DomainFilter -contains $_.EmailAddress.Split('@')[1] }) #1.0.83
			[int]$count = $count - $StudentList.Count
			write-host "Filtering source Students against $($iniFile.StudentSourceGroups.DomainFilter.Count) Email Domain filters: $($iniFile.StudentSourceGroups.DomainFilter)" -foreground green
			write-host "$count Students Removed from Processing by Students Email Domain Filters" -foregroundcolor $alttextcolour
			if ($DetailedLoggingEnabled) {
				Add-Content -Path "$LogFile" -Value "Filtering source Students against $($iniFile.StudentSourceGroups.DomainFilter.Count) Email Domain filters: $($iniFile.StudentSourceGroups.DomainFilter)" -Encoding Unicode
				Add-Content -Path "$LogFile" -Value "$count Students Removed from Processing by Students Email Domain Filters" -Encoding Unicode
			}
			$Actions += "- $($StudentList.Count) Filtered Source Group Students to be processed that have one of these Email Domains: $($iniFile.StudentSourceGroups.DomainFilter)"
			if ($StudentList.Count -eq 0) {
				$Warnings += "- After filtering, there are no Students available from the Student Source Groups. Check the Student Email Domain filter is correct - $($iniFile.StudentSourceGroups.DomainFilter)"
				write-host "After filtering, there are no Students available from the Student Source Groups. Check the Student Email Domain filter is correct - $($iniFile.StudentSourceGroups.DomainFilter)" -foreground red
				if ($DetailedLoggingEnabled) {
					Add-Content -Path "$LogFile" -Value "After filtering, there are no Students available from the Student Source Groups. Check the Student Email Domain filter is correct - $($iniFile.StudentSourceGroups.DomainFilter)" -Encoding Unicode
				}
			} 
		} 
	}
} else {
	if ($NeedsStudentList -eq $false) {
		$Actions += "- Student Source Group Processing: Not required"
		write-host "Student Source Group Processing: Not required" -foreground yellow
		if ($DetailedLoggingEnabled) {
			Add-Content -Path "$LogFile" -Value "`r`nGetting the Source List of Students" -Encoding Unicode
			Add-Content -Path "$LogFile" -Value "===================================`r`n" -Encoding Unicode
			Add-Content -Path "$LogFile" -Value "`r`nGroup Rules Settings"
			Add-Content -Path "$LogFile" -Value  "--------------------"
			$GroupRules | ft * -AutoSize | Out-String -Width 4096 | Out-File -filepath "$LogFile" -append -Encoding Unicode			
			Add-Content -Path "$LogFile" -Value "`r`nStudent Source Group Processing: Not required" -Encoding Unicode			
			
		}
	}
}


$count = $null

if ($MajorErrors.Count -eq 0 -and $NeedsTeacherList -eq $true -and $NeedsStudentList -eq $true) {
	if ($iniFile.TeacherSourceGroups.DontProcessStudentsFoundinTeacherSourceGroups -eq $true -or $iniFile.TeacherSourceGroups.DontProcessStudentsFoundinTeacherSourceGroups -eq 1 ) {
		if ($TeacherList.count -gt 0 -and $StudentList.Count -gt 0) {
			[int]$count = $TeacherList.Count
			$TeacherList = @($TeacherList | Where { $_.ObjectID -notin $StudentList.ObjectID })
			[int]$count = $count - $TeacherList.Count
			$Actions += "- Dont Process Students Found in Teachers Source Group: $Count Students removed - $($TeacherList.count) Teachers remaining"
			write-host "Dont Process Students Found in Teachers Source Group: $Count Students removed - $($TeacherList.count) Teachers remaining" -foreground $alttextcolour
            if ($DetailedLoggingEnabled) {
                Add-Content -Path "$LogFile" -Value "Dont Process Students Found in Teachers Source Group: $Count Students removed - $($TeacherList.count) Teachers remaining" -Encoding Unicode
            }
		} else {
			$Actions += "- Dont Process Students Found in Teachers Source Group: No Students found for removing from Teachers"
			write-host "Dont Process Students Found in Teachers Source Group: No Students found for removing from Teachers" -foreground yellow
			if ($DetailedLoggingEnabled) {
                Add-Content -Path "$LogFile" -Value "Dont Process Students Found in Teachers Source Group: No Students found for removing from Teachers" -Encoding Unicode
            }
		}
	}
	
	if ($iniFile.StudentSourceGroups.DontProcessTeachersFoundinStudentSourceGroups -eq $true -or $iniFile.StudentSourceGroups.DontProcessTeachersFoundinStudentSourceGroups -eq 1 ) {
		if ($StudentList.Count -gt 0 -and $TeacherList.count -gt 0) {
			[int]$count = $StudentList.Count
			$StudentList = @($StudentList | Where { $_.ObjectID -notin $TeacherList.ObjectID })
			[int]$count = $count - $StudentList.Count
			$Actions += "- Dont Process Teachers Found in Students Source Group: $Count Teachers removed - $($StudentList.count) Students remaining"
			write-host "Dont Process Teachers Found in Students Source Group: $Count Teachers removed - $($StudentList.count) Students remaining" -foreground $alttextcolour
            if ($DetailedLoggingEnabled) {
                Add-Content -Path "$LogFile" -Value "Dont Process Teachers Found in Students Source Group: $Count Teachers removed - $($StudentList.count) remainingg" -Encoding Unicode
            }
		} else {
			$Actions += "- Dont Process Teachers Found in Students Source Group: No Teachers found for removing from Students"
			write-host "Dont Process Teachers Found in Students Source Group: No Teachers found for removing from Students" -foreground yellow
			if ($DetailedLoggingEnabled) {
                Add-Content -Path "$LogFile" -Value "Dont Process Teachers Found in Students Source Group: No Teachers found for removing from Students" -Encoding Unicode
            }
		}
	}
}

if ($MajorErrors.Count -eq 0 -and $DetailedLoggingEnabled) {
	Add-Content -Path "$LogFile" -Value "" -Encoding Unicode
}

if ($MajorErrors.Count -eq 0 -and $TeacherList.Count -gt 0) {
	write-host "$($SimString)$($TeacherList.Count) Teachers available for processing" -foreground $alttextcolour
	$Actions += "- $($SimString)$($TeacherList.Count) Teachers available for processing"
	if ($DetailedLoggingEnabled) {
		Add-Content -Path "$LogFile" -Value "$($SimString)$($TeacherList.Count) Teachers available for processing" -Encoding Unicode
	}	
	if ($iniFile.TeacherSourceGroups.LogAvailableUsers -eq $true -or $iniFile.TeacherSourceGroups.LogAvailableUsers -eq 1 -or $Verbose -eq $true ) {
		if ($DetailedLoggingEnabled) {
			Add-Content -Path "$LogFile" -Value "`r`nTeachers that are available for Processing" -Encoding Unicode
			Add-Content -Path "$LogFile" -Value "==========================================`r`n" -Encoding Unicode
			Process-UserCSVOutput ($TeacherList | Select CommonName, DisplayName, EmailAddress, GroupMemberType, IsLicensed, LastDirSyncTime, ObjectID, OverallProvisioningStatus, ValidationStatus )    
		}
	}
}


if ($MajorErrors.Count -eq 0 -and $StudentList.Count -gt 0) {
	write-host "$($SimString)$($StudentList.Count) Students available for processing" -foreground $alttextcolour
	$Actions += "- $($SimString)$($StudentList.Count) Students available for processing"
	if ($DetailedLoggingEnabled) {
		Add-Content -Path "$LogFile" -Value "$($SimString)$($StudentList.Count) Students available for processing" -Encoding Unicode
	}
	if ($iniFile.StudentSourceGroups.LogAvailableUsers -eq $true -or $iniFile.StudentSourceGroups.LogAvailableUsers -eq 1 -or $Verbose -eq $true ) {
		if ($DetailedLoggingEnabled) {
			Add-Content -Path "$LogFile" -Value "`r`nStudents that are available for Processing" -Encoding Unicode
			Add-Content -Path "$LogFile" -Value "==========================================`r`n" -Encoding Unicode
			Process-UserCSVOutput ($StudentList | Select CommonName, DisplayName, EmailAddress, GroupMemberType, IsLicensed, LastDirSyncTime, ObjectID, OverallProvisioningStatus, ValidationStatus )    
		}
	}
}


if ($MajorErrors.Count -eq 0 -and ($TeacherList.Count -eq 0 -and $NeedsTeacherList -eq $true) -and ($StudentList.Count -eq 0 -$NeedsStudentList -eq $true) ) {
    $Warnings += "- No Source Students or Teachers to Process"
    write-host "No Source Students or Teachers to Process" -foreground red
    if ($DetailedLoggingEnabled) {
        Add-Content -Path "$LogFile" -Value "No Source Students or Teachers to Process" -Encoding Unicode
    }
}


if ($MajorErrors.Count -eq 0 -and ($NeedsConnecttoExchangeOnline -eq $true -or $NeedsAzureADPreview -eq $true -or $NeedsMicrosoftTeams -eq $true)) {
    if ($DetailedLoggingEnabled) {
	    Add-Content -Path "$LogFile" -Value "`r`nConnecting to Additional Services" -Encoding Unicode
        Add-Content -Path "$LogFile" -Value "=================================`r`n" -Encoding Unicode
    }
}

# Connect to Exchange Online
if ($MajorErrors.Count -eq 0 -and $NeedsConnecttoExchangeOnline -eq $true) {
	Import-Module ExchangeOnlineManagement
	$e = $error.count
	if ((get-module | where { $_.Name -eq 'ExchangeOnlineManagement' } | select -First 1).Version.Major -ge 3) {
		write-host "Connecting to Exchange Online V3" -foregroundcolor green
		#Connect-ExchangeOnline -Credential $GACreds -UseRPSSession -ShowBanner:$false -ShowProgress:$false -Verbose:$false # -WarningAction SilentlyContinue -ErrorAction SilentlyContinue #| out-null
		Connect-ExchangeOnline -Credential $GACreds -ShowBanner:$false -ShowProgress:$false -Verbose:$false # -WarningAction SilentlyContinue -ErrorAction SilentlyContinue #| out-null	# 1.0.82
	   if (@(Get-ConnectionInformation).count -gt 0) {
			$ConnectedtoMicrosoftExchange = $true
			$Actions += "- Connected to Exchange Online V3"
			if ($DetailedLoggingEnabled) {
				Get-ConnectionInformation | fl * | Out-String -Width 4096 | Out-File -filepath "$LogFile" -append -Encoding Unicode
				Add-Content -Path "$LogFile" -Value "Connected to Exchange Online V3`r`n" -Encoding Unicode
			}
		} else {
			$errorstring = ""
			if ($error.count -gt $e) {
				if ($Error[0].Exception.Message) {
					$errorstring = $Error[0].Exception.Message
				} else {
					$errorstring = "(unknown)"
				}
			} else {
				$errorstring = "(unknown)"	
			}
			
			$MajorErrors += "- Could not connect to Exchange Online V3. Error message: $errorstring"
			write-host "Could not connect to Exchange Online V3. Error message: $errorstring" -foreground red
			if ($DetailedLoggingEnabled) {
				Add-Content -Path "$LogFile" -Value "Could not connect to Exchange Online V3. Error message: $errorstring" -Encoding Unicode
			}
		}
	} else {
		write-host "Connecting to Exchange Online V2" -foregroundcolor green
		Connect-ExchangeOnline -Credential $GACreds -ShowBanner:$false -ShowProgress:$false -Verbose:$false # -WarningAction SilentlyContinue -ErrorAction SilentlyContinue #| out-null
	
	   if (( get-pssession | where {$_.State -eq 'Opened' -and $_.ConfigurationName -eq 'Microsoft.Exchange'}).Count -gt 0) {
			$ConnectedtoMicrosoftExchange = $true
			$Actions += "- Connected to Exchange Online V2"
			if ($DetailedLoggingEnabled) {
				get-pssession | where {$_.State -eq 'Opened' -and $_.ConfigurationName -eq 'Microsoft.Exchange'}| ft * -AutoSize | Out-String -Width 4096 | Out-File -filepath "$LogFile" -append -Encoding Unicode
				Add-Content -Path "$LogFile" -Value "Connected to Exchange Online V2`r`n" -Encoding Unicode
			}	
		} else {
			$errorstring = ""
			if ($error.count -gt $e) {
				if ($Error[0].Exception.Message) {
					$errorstring = $Error[0].Exception.Message
				} else {
					$errorstring = "(unknown)"
				}
			} else {
				$errorstring = "(unknown)"	
			}
			
			$MajorErrors += "- Could not connect to Exchange Online V2. Error message: $errorstring"
			write-host "Could not connect to Exchange Online V2. Error message: $errorstring" -foreground red
			if ($DetailedLoggingEnabled) {
				Add-Content -Path "$LogFile" -Value "Could not connect to Exchange Online V2. Error message: $errorstring" -Encoding Unicode
			}
		}
	}
}

#      Disconnect-ExchangeOnline -Confirm:$false -Verbose:$false -WarningAction SilentlyContinue -ErrorAction SilentlyContinue | out-null


$tokenPolicy = $null

# Connect to Azure AD
if ($MajorErrors.Count -eq 0 -and $NeedsAzureADPreview -eq $true) {
    write-host "Connecting to AzureAD" -foregroundcolor green
    $e = $error.count
	$AzureAD = AzureADPreview\Connect-AzureAD -Credential $GACreds
    if ($AzureAD) {
        $ConnectedtoAzureAD = $true
        $Actions += "- Connected to Azure AD: Account: $($AzureAD.Account)  Environment: $($AzureAD.Environment)  Tenant: $($AzureAD.Tenant)   TenantID: $($AzureAD.TenantID)   TenantDomain: $($AzureAD.TenantDomain)"
        if ($DetailedLoggingEnabled) {
            $AzureAD | ft * -AutoSize | Out-String -Width 4096 | Out-File -filepath "$LogFile" -append -Encoding Unicode
            Add-Content -Path "$LogFile" -Value "Connected to Azure AD: Account: $($AzureAD.Account)" -Encoding Unicode
        }

# set connection policy 
    if ($iniFile.Misc.CreateAzureADAccessPolicy -eq $true -or $iniFile.Misc.CreateAzureADAccessPolicy -eq 1) {
        $e = $error.count
        try {
            $aadTokenLifetimePolicies = @(AzureADPreview\Get-AzureADPolicy | Where-Object {$_.Type -eq "TokenLifetimePolicy"} | Select-Object ID, DisplayName, Type, IsOrganizationDefault, Definition)
            if ($DetailedLoggingEnabled) {
                Add-Content -Path "$LogFile" -Value "`r`nExisting Azure AD Token Lifetime Policies:" -Encoding Unicode
                Add-Content -Path "$LogFile" -Value "------------------------------------------" -Encoding Unicode
                
                if ($aadTokenLifetimePolicies.count -lt 1) {
                    Add-Content -Path "$LogFile" -Value "(none)`r`n" -Encoding Unicode
                } else {
                    $aadTokenLifetimePolicies | ft * -AutoSize | Out-String -Width 4096 | Out-File -filepath "$LogFile" -append -Encoding Unicode
                }
            }

            # v1.0.1 leave any existing STEPER policies in place
            $aadTokenLifetimePolicies = @(AzureADPreview\Get-AzureADPolicy | Where-Object { $_.DisplayName -eq "STEPER-AccessTokenLifetime"})
    #            foreach ($ADPolicy in $aadTokenLifetimePolicies) {
    #            #write-host "done"
    #                Remove-AzureADPolicy -id $ADPolicy.id # there should only ever be one policy with the same name - if its shown here, its from a previous killed run
    #            }

            if ($aadTokenLifetimePolicies.count -lt 1) {
                $aadTokenLifetimePolicies = @(AzureADPreview\Get-AzureADPolicy | Where-Object {$_.Type -eq "TokenLifetimePolicy"} ) #| Select-Object ID, DisplayName, Type, IsOrganizationDefault, Definition

                if ((! $aadTokenLifetimePolicies) -or ($aadTokenLifetimePolicies.Count -eq 0) -or ($iniFile.Misc.ApplyAzureADAccessPolicyWhenPoliciesAlreadyExist -eq $true) -or ($iniFile.Misc.ApplyAzureADAccessPolicyWhenPoliciesAlreadyExist -eq 1)) {
                    $tokenPolicy = AzureADPreview\New-AzureADPolicy -Definition @('{"TokenLifetimePolicy":{"Version":1,"AccessTokenLifetime":"1.00:00:00"}}') -DisplayName "STEPER-AccessTokenLifetime" -IsOrganizationDefault $true -Type "TokenLifetimePolicy"
                    Set-RegistryValue -Path $regpath -Name 'UsingAzureADAccessPolicy' -ValueType DWORD -Value 2

        # 0 not required, or not in use
        # 1 required, or in use
        # 2 required, policy set by this instance / profile, in use

                    if ($DetailedLoggingEnabled) {
                        Add-Content -Path "$LogFile" -Value "Created temporary Azure AD Policy for Access Token Timeout" -Encoding Unicode
						Add-Content -Path "$LogFile" -Value "Policy ID         : $($tokenPolicy.ID)" -Encoding Unicode
						Add-Content -Path "$LogFile" -Value "Policy DisplayName: $($tokenPolicy.Displayname)`r`n" -Encoding Unicode
                        }
                    $Information += "- Created temporary Azure AD Policy for Access Token Timeout: ID: $($tokenPolicy.ID)   DisplayName: $($tokenPolicy.Displayname)"
                    write-host "Created temporary Azure AD Policy for Access Token Timeout" -foreground yellow
					write-host "Policy ID         : $($tokenPolicy.ID)" -foreground yellow
					write-host "Policy DisplayName: $($tokenPolicy.Displayname)" -foreground yellow
                    }
                } else {
                    $Information += "- Re-using existing temporary Azure AD Policy for Access Token Timeout"
                    write-host "Re-using existing temporary Azure AD Policy for Access Token Timeout" -foreground yellow
                }
            }
        catch {
            $errorstring = ""	
            if ($error.count -gt $e) {
                if ($Error[0].Exception.Message) {
				    $errorstring = $Error[0].Exception.Message
			    } else {
    				$errorstring = "(unknown)"
	    		}
            }
            Set-RegistryValue -Path $regpath -Name 'UsingAzureADAccessPolicy' -ValueType DWORD -Value 0
            if ($iniFile.Misc.ThrowWarningInsteadofErrorOnAzureADAccessPolicyFailure -eq $true -or $iniFile.Misc.ThrowWarningInsteadofErrorOnAzureADAccessPolicyFailure -eq 1) { 
                $Warnings+= "- Unable to set Temporary Azure AD Policy for Access Token Timeout - error message: $errorstring"
                if ($DetailedLoggingEnabled) {
                    Add-Content -Path "$LogFile" -Value "`r`nUnable to set Temporary Azure AD Policy for Access Token Timeout - error message: $errorstring`r`n" -Encoding Unicode
                    }
                write-host "WARNING: Unable to set Temporary Azure AD Policy for Access Token Timeout - error message: $errorstring" -foreground red
            } else {
                $MajorErrors+= "- ERROR: Unable to set Temporary Azure AD Policy for Access Token Timeout - error message: $errorstring"
                if ($DetailedLoggingEnabled) {
                    Add-Content -Path "$LogFile" -Value "`r`nUnable to set Temporary Azure AD Policy for Access Token Timeout - error message: $errorstring`r`n" -Encoding Unicode
                    }
                write-host "ERROR: Unable to set Temporary Azure AD Policy for Access Token Timeout - error message: $errorstring" -foreground red
                }
            }
        } else {
            Set-RegistryValue -Path $regpath -Name 'UsingAzureADAccessPolicy' -ValueType DWORD -Value 0 # 1.0.52 this could probably be disabled
        }


    } else {
		$errorstring = ""
		if ($error.count -gt $e) {
			if ($Error[0].Exception.Message) {
				$errorstring = $Error[0].Exception.Message
			} else {
				$errorstring = "(unknown)"
			}
		} else {
			$errorstring = "(unknown)"	
		}
        $MajorErrors += "- Could not connect to AzureAD. Check the credentials and version of the AzureAD Powershell Module. Error message: $errorstring"
        write-host "Could not connect to AzureAD. Check the credentials and version of the AzureAD Powershell Module. Error message: $errorstring" -foreground red
        if ($DetailedLoggingEnabled) {
            Add-Content -Path "$LogFile" -Value "Could not connect to AzureAD. Check the credentials and version of the AzureAD Powershell Module. Error message: $errorstring" -Encoding Unicode
        }
		Set-RegistryValue -Path $regpath -Name 'UsingAzureADAccessPolicy' -ValueType DWORD -Value 0 # 1.0.52
    }
}


# Connect to Teams
if ($MajorErrors.Count -eq 0 -and $NeedsMicrosoftTeams -eq $true) {
    write-host "Connecting to Microsoft Teams" -foregroundcolor green

	if ($iniFile.Misc.IgnoreMissingNETFramework -ne $true -and $iniFile.Misc.IgnoreMissingNETFramework -ne 1) {
		$NetRelease = get-registryvalue -Path 'HKLM:\SOFTWARE\Microsoft\NET Framework Setup\NDP\v4\Full' -Name Release -ValueType DWORD
		$NetVersion = get-registryvalue -Path 'HKLM:\SOFTWARE\Microsoft\NET Framework Setup\NDP\v4\Full' -Name Version -ValueType String
		if (!$NetRelease) {
			$NetRelease = "(not installed)"
			$Warnings += "- Microsoft .NET v4 does not appear to be installed. The Microsoft Teams Powershell Module may not function"
			write-host "Microsoft .NET v4 does not appear to be installed. The Microsoft Teams Powershell Module may not function" -foreground yellow
			if ($DetailedLoggingEnabled) {
				Add-Content -Path "$LogFile" -Value "Microsoft .NET v4 does not appear to be installed. The Microsoft Teams Powershell Module may not function" -Encoding Unicode
			}
		} else {
			if ($NetRelease -le 460805) {
				$Warnings += "- The installed Microsoft .NET v4 release $NetRelease / version $NetVersion may not be high enough to support the Microsoft Teams Powershell Module"
				write-host "The installed Microsoft .NET v4 release $NetRelease / version $NetVersion may not be high enough to support the Microsoft Teams Powershell Module" -foreground yellow
				if ($DetailedLoggingEnabled) {
					Add-Content -Path "$LogFile" -Value "The installed Microsoft .NET v4 release $NetRelease / version $NetVersion may not be high enough to support the Microsoft Teams Powershell Module" -Encoding Unicode
				}
			}			
		}
	}

    $Teams = Connect-MicrosoftTeams -Credential $GACreds
    $e = $error.count
	if ($Teams) {
        $ConnectedtoTeams = $true
        $Actions += "- Connected to Microsoft Teams: Account: $($Teams.Account)  Environment: $($Teams.Environment)  Tenant: $($Teams.Tenant)   TenantID: $($Teams.TenantID)"
        if ($DetailedLoggingEnabled) {
            $Teams | ft * -AutoSize | Out-String -Width 4096 | Out-File -filepath "$LogFile" -append -Encoding Unicode
            Add-Content -Path "$LogFile" -Value "Connected to Microsoft Teams: Account: $($Teams.Account)" -Encoding Unicode
			Add-Content -Path "$LogFile" -Value "Microsoft .NET v4 release: $NetRelease`r`n" -Encoding Unicode
        }
    } else {
		$errorstring = ""
		if ($error.count -gt $e) {
			if ($Error[0].Exception.Message) {
				$errorstring = $Error[0].Exception.Message
			} else {
				$errorstring = "(unknown)"
			}
		} else {
			$errorstring = "(unknown)"	
		}
        $MajorErrors += "- Could not connect to Microsoft Teams. Check the credentials and version of the Teams Powershell Module. Error message: $errorstring"
        write-host "Could not connect to Microsoft Teams. Check the credentials and version of the Teams Powershell Module. Error message: $errorstring" -foreground red
        if ($DetailedLoggingEnabled) {
                Add-Content -Path "$LogFile" -Value "Could not connect to Microsoft Teams. Check the credentials and version of the Teams Powershell Module. Error message: $errorstring" -Encoding Unicode
        }
    }
}


#re-do after adding modules
$Global:ProgressPreference = 'SilentlyContinue'
$ErrorActionPreference= 'silentlycontinue'
$WarningPreference = 'SilentlyContinue'


# Apply Teacher Licensing
$stafflicensingmembers = @()
if ($MajorErrors.Count -eq 0 -and $TeacherList.Count -gt 0 -and ($iniFile.LicensingGroups.AssignTeacherstoLicensingGroup -eq $true -or $iniFile.LicensingGroups.AssignTeacherstoLicensingGroup -eq 1)) {
    write-host "Adding Teachers to the Licensing Group" -foregroundcolor Green        
    if ($DetailedLoggingEnabled) {
        Add-Content -Path "$LogFile" -Value "`r`nAdding Teachers to the Licensing Group" -Encoding Unicode
        Add-Content -Path "$LogFile" -Value "======================================`r`n" -Encoding Unicode
        Add-Content -Path "$LogFile" -Value "Search Group Name: $($iniFile.LicensingGroups.AllTeachersLicensingGroupName)`r`n" -Encoding Unicode
    }
    $stafflicensinggroup = $null
    $TeacherstoAddLicensing = @()
    $TeachersNotInLicensingGroup = @()
    $AssignmentErrors = @()
    $AssignmentErrorsCaptured=0
    #$stafflicensinggroup = @(Get-MsolGroup -SearchString $iniFile.LicensingGroups.AllTeachersLicensingGroupName)
    if ((Test-IsGuid -ObjectGuid $iniFile.LicensingGroups.AllTeachersLicensingGroupName) -eq $false) {
        $stafflicensinggroup = @(Get-MsolGroup -SearchString $iniFile.LicensingGroups.AllTeachersLicensingGroupName | where {$_.GroupType -eq 'Security' -or $_.GroupType -eq 'MailEnabledSecurity'}) # 1.0.21

        if ($iniFile.LicensingGroups.DisableTeacherGroupNameorIDChecking -ne $true -and $iniFile.LicensingGroups.DisableTeacherGroupNameorIDChecking -ne 1) {
	        $stafflicensinggroup = $stafflicensinggroup | where { $_.CommonName -like $iniFile.LicensingGroups.AllTeachersLicensingGroupName -or $_.DisplayName -like $iniFile.LicensingGroups.AllTeachersLicensingGroupName -or $_.EmailAddress -like $iniFile.LicensingGroups.AllTeachersLicensingGroupName } # 1.0.21
        }
    } else {
        $stafflicensinggroup = @(Get-MsolGroup -ObjectID $iniFile.LicensingGroups.AllTeachersLicensingGroupName)
    }
    
	#new
	$GroupCreated=$False
	if ($stafflicensinggroup.Count -eq 0 -or !($stafflicensinggroup)) {
		if ($iniFile.LicensingGroups.CreateAllTeachersLicensingGroupIfNotExists -eq $true -or $iniFile.LicensingGroups.CreateAllTeachersLicensingGroupIfNotExists -eq 1) {
            if ((Test-IsGuid -ObjectGuid $iniFile.LicensingGroups.AllTeachersLicensingGroupName) -eq $false) {

			    if ($MakeChanges -eq $true) {
				    $stafflicensinggroup=New-MsolGroup -DisplayName $iniFile.LicensingGroups.AllTeachersLicensingGroupName -Description "Group for use with Teacher Licensing - Created by STEPER"
                    if ($stafflicensinggroup) {
                        $GroupCreated=$True
				        if ($DetailedLoggingEnabled) {
					        Add-Content -Path "$LogFile" -Value "Teacher Licensing Group $($iniFile.LicensingGroups.AllTeachersLicensingGroupName) was missing and has been created" -Encoding Unicode
				        }
				        $Actions += "- Teacher Licensing Group $($iniFile.LicensingGroups.AllTeachersLicensingGroupName) was missing and has been created"
				        write-host "Teacher Licensing Group $($iniFile.LicensingGroups.AllTeachersLicensingGroupName) was missing and has been created" -ForegroundColor yellow
                    } else {
                        if ($DetailedLoggingEnabled) {
    					    Add-Content -Path "$LogFile" -Value "Teacher Licensing Group $($iniFile.LicensingGroups.AllTeachersLicensingGroupName) was missing but could not be created" -Encoding Unicode
				        }
				        $Warnings += "- Teacher Licensing Group $($iniFile.LicensingGroups.AllTeachersLicensingGroupName) was missing but could not be created"
				        write-host "Teacher Licensing Group $($iniFile.LicensingGroups.AllTeachersLicensingGroupName) was missing but could not be created" -ForegroundColor red
                    }
			    } else {
				    if ($DetailedLoggingEnabled) {
					    Add-Content -Path "$LogFile" -Value "Simulation: Teacher Licensing Group $($iniFile.LicensingGroups.AllTeachersLicensingGroupName) was missing and would have been created" -Encoding Unicode
				    }
				    $Actions += "- Simulation: Teacher Licensing Group $($iniFile.LicensingGroups.AllTeachersLicensingGroupName) was missing and would have been created"
				    write-host "Simulation: Teacher Licensing Group $($iniFile.LicensingGroups.AllTeachersLicensingGroupName) was missing and would have been created" -ForegroundColor cyan
                    $GroupCreated=$True
			    }

            } else {
                if ($DetailedLoggingEnabled) {
    			    Add-Content -Path "$LogFile" -Value "- Teacher Licensing Group $($iniFile.LicensingGroups.AllTeachersLicensingGroupName) was missing but could not be created because an ObjectID was provided" -Encoding Unicode
			    }
				$Warnings += "- Teacher Licensing Group $($iniFile.LicensingGroups.AllTeachersLicensingGroupName)  was missing but could not be created because an ObjectID was provided"
				write-host "Teacher Licensing Group $($iniFile.LicensingGroups.AllTeachersLicensingGroupName)  was missing but could not be created because an ObjectID was provided" -ForegroundColor red
            }
		}
	} else {
		if ($stafflicensinggroup.Count -eq 1) {
			$stafflicensingmembers = @(get-msolgroupmember -GroupObjectId $stafflicensinggroup.objectid -All)
		}
	}
	
	if ($stafflicensinggroup.Count -eq 1 -or $GroupCreated -eq $true) {
        if ($DetailedLoggingEnabled) {
            Add-Content -Path "$LogFile" -Value "Resulting Group DisplayName: $($stafflicensinggroup.DisplayName)" -Encoding Unicode
			Add-Content -Path "$LogFile" -Value "Resulting Group ObjectID: $($stafflicensinggroup.objectid)" -Encoding Unicode
        }		
        if (($stafflicensingmembers.Count -eq 0) -or !($stafflicensingmembers)) {
            
			#new
			if ($GroupCreated -eq $false) {
				$Warnings += "- There are no Teachers already in the Licensing Group $($iniFile.LicensingGroups.AllTeachersLicensingGroupName)"
			} else {
				$Information += "- There are no Teachers already in the newly created Licensing Group $($iniFile.LicensingGroups.AllTeachersLicensingGroupName)"
			}
            $TeachersNotInLicensingGroup = $TeacherList # was disabled?
        } else {
            $Actions += "- $($stafflicensingmembers.Count) Teachers Retrieved from MSOnline that are members of the Licensing Group $($iniFile.LicensingGroups.AllTeachersLicensingGroupName)"
			$TeachersNotInLicensingGroup = @($TeacherList | where { $_.ObjectID -notin $stafflicensingmembers.ObjectID }) # 1.0.94
        }
        if ($TeachersNotInLicensingGroup.count -gt 0) {
            $TeachersNotInLicensingGroup  = @($TeachersNotInLicensingGroup | Sort EmailAddress)
        }

        $TeacherstoAddLicensing = $TeachersNotInLicensingGroup
        if ($iniFile.LicensingGroups.OnlyApplytoUnlicensedTeachers -eq $true -or $iniFile.LicensingGroups.OnlyApplytoUnlicensedTeachers -eq 1) {
            $TeacherstoAddLicensing = @($TeacherstoAddLicensing | where { $_.IsLicensed -eq $false })
        }

		if ($TeacherstoAddLicensing.Count -gt 0) {
			$string = $TeacherstoAddLicensing[0].EmailAddress
			if ($MakeChanges -eq $true) {
    		    if ($DetailedLoggingEnabled) {
					Process-UserCSVOutput ($TeacherstoAddLicensing | Select DisplayName, EmailAddress, GroupMemberType, IsLicensed, LastDirSyncTime, ObjectID, OverallProvisioningStatus, ValidationStatus)
				}
				
				[int]$ResultCount=0
				[int]$personprocessed=0
				$funcstarttime = (Get-Date)
				
				foreach ($Teacher in $TeacherstoAddLicensing) {
                    $e = $error.count

    				Add-MsolGroupMember -GroupObjectId $stafflicensinggroup.objectid -GroupMemberType User -GroupMemberObjectId $Teacher.objectid -ErrorAction SilentlyContinue
					$ResultCount+=1
					$personprocessed = $personprocessed + 1

					if ($error.count -gt $e) {
						$errorstring = ""
						if ($Error[0].Exception.Message) {
							$errorstring = $Error[0].Exception.Message
						} else {
							$errorstring = "(unknown)"
						}
						$AssignmentErrors += [PSCustomObject]@{
						    'DisplayName' = $Teacher.DisplayName
                            'EmailAddress' = $Teacher.EmailAddress
                            'UserType' = 'Teacher'						
                            'ObjectID' = $Teacher.objectid
                            'IsLicensed' = $Teacher.IsLicensed
                            'GroupObjectID' = $stafflicensinggroup.objectid
						    'Error' = $errorstring
						}
				        $AssignmentErrorsCaptured+=1				
                        if ($AssignmentErrorsCaptured -le $RepeatLogEventThreshold) {
    						write-host "Failed to add Teacher to the Licensing Group $($iniFile.LicensingGroups.AllTeachersLicensingGroupName) - $($Teacher.EmailAddress)" -ForegroundColor Red
                        }
                        if ($AssignmentErrorsCaptured -eq $RepeatLogEventThreshold) {
                            write-host "RepeatLogEventThereshold: Further Teacher Licensing Group Assignment errors will be supressed" -ForegroundColor yellow
                        }
											
					}

					if ($ResultCount -eq $LongJobUpdateInterval) {

						[int]$ResultCount=0

                        $UsersProcessedString, $timestring, $timeremainingseconds = get-timeelapsed -StartTime $funcstarttime -Processed $personprocessed -ToProcess ($TeacherstoAddLicensing.Count)

						if ($timeremainingseconds -ge 10) {
							write-host "Adding Teachers to the Licensing Group - Processed $UsersProcessedString of $($TeacherstoAddLicensing.Count) users - ETR$timestring" -ForegroundColor Gray
							if ($DetailedLoggingEnabled) {
								Add-Content -Path "$LogFile" -Value "Adding Teachers to the Licensing Group - Processed $UsersProcessedString of $($TeacherstoAddLicensing.Count) users - ETR$timestring" -Encoding Unicode  
							}
						}
					}					
				}

                if ( @($AssignmentErrors).count -gt 0) {
                    if ($DetailedLoggingEnabled) {
				        Add-Content -Path "$LogFile" -Value "`r`nAdd to Licensing Group Errors:" -Encoding Unicode
				        Add-Content -Path "$LogFile" -Value "------------------------------" -Encoding Unicode
				        Process-UserCSVOutput ($AssignmentErrors  | Sort DisplayName, EmailAddress, IsLicensed)
                        Add-Content -Path "$LogFile" -Value "$($AssignmentErrors.count) Add to Licensing Group Errors were encountered during Teacher License Group Processing" -Encoding Unicode
                    }
				    write-host "$($AssignmentErrors.count) Add to Licensing Group Errors were encountered during Teacher License Group Processing" -ForegroundColor Red
				    $warnings += "- $($AssignmentErrors.count) Add to Licensing Group Errors were encountered during Teacher License Group Processing"

			    } else {
				    if ($DetailedLoggingEnabled) {
    			        Add-Content -Path "$LogFile" -Value "$($TeacherstoAddLicensing.Count) Teachers Added to the Licensing Group $($iniFile.LicensingGroups.AllTeachersLicensingGroupName), including $string" -Encoding Unicode
			        }
				    $Actions += "- $($TeacherstoAddLicensing.Count) Teachers Added to the Licensing Group $($iniFile.LicensingGroups.AllTeachersLicensingGroupName), including $string"
                    write-host "$($TeacherstoAddLicensing.Count) Teachers Added to the Licensing Group $($iniFile.LicensingGroups.AllTeachersLicensingGroupName), including $string" -ForegroundColor yellow
                }

			} else {
    			if ($DetailedLoggingEnabled) {
			        Process-UserCSVOutput ($TeacherstoAddLicensing | Select DisplayName, EmailAddress, GroupMemberType, IsLicensed, LastDirSyncTime, ObjectID, OverallProvisioningStatus, ValidationStatus)
                    Add-Content -Path "$LogFile" -Value "Simulation: $($TeacherstoAddLicensing.Count) Teachers would have been added to the Licensing Group $($iniFile.LicensingGroups.AllTeachersLicensingGroupName), including $string" -Encoding Unicode
		        }
			    $Actions += "- Simulation: $($TeacherstoAddLicensing.Count) Teachers would have been added to the Licensing Group $($iniFile.LicensingGroups.AllTeachersLicensingGroupName), including $string"
                write-host "Simulation: $($TeacherstoAddLicensing.Count) Teachers would have been added to the Licensing Group $($iniFile.LicensingGroups.AllTeachersLicensingGroupName), including $string" -ForegroundColor cyan
			}
            $results += [PSCustomObject]@{
                'Type' = 'Teacher'
				'Quantity' = $($TeacherstoAddLicensing.Count)
				'Source' = "$TeacherSourceGroupCount TeacherSourceGroups"
                'Action' = "$($SimString)Add to Teacher Licensing Group"
                'Target' = $iniFile.LicensingGroups.AllTeachersLicensingGroupName
                'OtherInfo' = ''
                'AssignmentErrors' = $AssignmentErrors.count
            }
        } else {
    	    $Actions += "- There are no Teachers to add to the Licensing Group $($iniFile.LicensingGroups.AllTeachersLicensingGroupName)"
            write-host "There are no Teachers to add to the Licensing Group $($iniFile.LicensingGroups.AllTeachersLicensingGroupName)" -foreground white
            if ($DetailedLoggingEnabled) {
                Add-Content -Path "$LogFile" -Value "There are no Teachers to add to the Licensing Group $($iniFile.LicensingGroups.AllTeachersLicensingGroupName)" -Encoding Unicode
            }
		}
    } else {
        if ($stafflicensinggroup.Count -gt 1) {
            $Warnings += "- More than one Licensing Group matches the name $($iniFile.LicensingGroups.AllTeachersLicensingGroupName), please check the name. Teacher Licensing will not be processed"
            write-host "More than one Licensing Group matches the name $($iniFile.LicensingGroups.AllTeachersLicensingGroupName), please check the name. Teacher Licensing will not be processed" -foreground red
            if ($DetailedLoggingEnabled) {
                Add-Content -Path "$LogFile" -Value "More than one Licensing Group matches the name $($iniFile.LicensingGroups.AllTeachersLicensingGroupName), please check the name. Teacher Licensing will not be processed" -Encoding Unicode
            }
        } else {
            $Warnings += "- The Teacher Licensing Group $($iniFile.LicensingGroups.AllTeachersLicensingGroupName) does not exist, Teacher Licensing will not be processed"
            write-host "The Teacher Licensing Group $($iniFile.LicensingGroups.AllTeachersLicensingGroupName) does not exist, Teacher Licensing will not be processed" -foreground red
            if ($DetailedLoggingEnabled) {
                Add-Content -Path "$LogFile" -Value "The Teacher Licensing Group $($iniFile.LicensingGroups.AllTeachersLicensingGroupName) does not exist, Teacher Licensing will not be processed" -Encoding Unicode
            }
        }
    }
} else {
# Get Unlicensed Teachers for Other Functions
    if ($MajorErrors.Count -eq 0 -and $TeacherList.Count -gt 0 -and $NeedsTeacherLicensingList -eq $true) {
        $stafflicensingmembers = @()
        write-host "Getting the List of Teachers that are not in the licensing group" -foregroundcolor green  
        if ($DetailedLoggingEnabled) {
            Add-Content -Path "$LogFile" -Value "`r`nGetting the List of Teachers that are not in the Licensing Group" -Encoding Unicode
            Add-Content -Path "$LogFile" -Value "================================================================`r`n" -Encoding Unicode
            Add-Content -Path "$LogFile" -Value "Search Group Name: $($iniFile.LicensingGroups.AllTeachersLicensingGroupName)`r`n" -Encoding Unicode
        }
        $TeacherstoAddLicensing = @()
        $TeachersNotInLicensingGroup = @()
        $stafflicensinggroup = $null
        #$stafflicensinggroup = @(Get-MsolGroup -SearchString $iniFile.LicensingGroups.AllTeachersLicensingGroupName)
        if ((Test-IsGuid -ObjectGuid $iniFile.LicensingGroups.AllTeachersLicensingGroupName) -eq $false) {
            $stafflicensinggroup = @(Get-MsolGroup -SearchString $iniFile.LicensingGroups.AllTeachersLicensingGroupName | where {$_.GroupType -eq 'Security' -or $_.GroupType -eq 'MailEnabledSecurity'}) # 1.0.21

            if ($iniFile.LicensingGroups.DisableTeacherGroupNameorIDChecking -ne $true -and $iniFile.LicensingGroups.DisableTeacherGroupNameorIDChecking -ne 1) {
    	        $stafflicensinggroup = $stafflicensinggroup | where { $_.CommonName -like $iniFile.LicensingGroups.AllTeachersLicensingGroupName -or $_.DisplayName -like $iniFile.LicensingGroups.AllTeachersLicensingGroupName -or $_.EmailAddress -like $iniFile.LicensingGroups.AllTeachersLicensingGroupName } # 1.0.21
            }
        } else {
            $stafflicensinggroup = @(Get-MsolGroup -ObjectID $iniFile.LicensingGroups.AllTeachersLicensingGroupName)
        }

        if ($stafflicensinggroup.Count -eq 1) {
			if ($DetailedLoggingEnabled) {
				Add-Content -Path "$LogFile" -Value "Resulting Group DisplayName: $($stafflicensinggroup.DisplayName)" -Encoding Unicode
				Add-Content -Path "$LogFile" -Value "Resulting Group ObjectID: $($stafflicensinggroup.objectid)" -Encoding Unicode
			}
            $stafflicensingmembers = @(get-msolgroupmember -GroupObjectId $stafflicensinggroup.objectid -All)
            if (($stafflicensingmembers.Count -eq 0) -or !($stafflicensingmembers)) {
                $Warnings += "- There are no Teachers already in the Licensing Group $($iniFile.LicensingGroups.AllTeachersLicensingGroupName)"
                $TeachersNotInLicensingGroup = $TeacherList # was disabled?
            } else {
                $Actions += "- $($stafflicensingmembers.Count) Teachers Retrieved from MSOnline that are members of the Licensing Group $($iniFile.LicensingGroups.AllTeachersLicensingGroupName)"
				$TeachersNotInLicensingGroup = @($TeacherList | where { $_.ObjectID -notin $stafflicensingmembers.ObjectID }) # 1.0.94
            }
            if ($TeachersNotInLicensingGroup.Count -gt 0) {
                $TeachersNotInLicensingGroup = @($TeachersNotInLicensingGroup | Sort EmailAddress)

                $string = $TeachersNotInLicensingGroup[0].EmailAddress
    			if ($DetailedLoggingEnabled) {
			        Process-UserCSVOutput ($TeachersNotInLicensingGroup | Select DisplayName, EmailAddress, GroupMemberType, IsLicensed, LastDirSyncTime, ObjectID, OverallProvisioningStatus, ValidationStatus)
                    Add-Content -Path "$LogFile" -Value "Get List of Teachers Not in the Licensing Group: $($TeachersNotInLicensingGroup.Count) Teachers that are not in the Licensing Group $($iniFile.LicensingGroups.AllTeachersLicensingGroupName), including $string" -Encoding Unicode
		        }
			    $Actions += "- Get List of Teachers Not in the Licensing Group: $($TeachersNotInLicensingGroup.Count) Teachers that are not in the Licensing Group $($iniFile.LicensingGroups.AllTeachersLicensingGroupName), including $string"

                $results += [PSCustomObject]@{
                    'Type' = 'Teacher'
				    'Quantity' = $($TeachersNotInLicensingGroup.Count)
				    'Source' = "$TeacherSourceGroupCount TeacherSourceGroups"
                    'Action' = "$($SimString)Get Teachers not in the licensing group"
                    'Target' = ''
                    'OtherInfo' = ''
                    'AssignmentErrors' = ''
                }
            } else {
                $Actions += "- There are no Teachers not in the licensing Group $($iniFile.LicensingGroups.AllTeachersLicensingGroupName)"
                write-host "There are no Teachers not in the licensing Group $($iniFile.LicensingGroups.AllTeachersLicensingGroupName)" -foreground white
                if ($DetailedLoggingEnabled) {
                    Add-Content -Path "$LogFile" -Value "There are no Teachers not in the licensing Group $($iniFile.LicensingGroups.AllTeachersLicensingGroupName)" -Encoding Unicode
                }
            }
        } else {
            if ($stafflicensinggroup.Count -gt 1) {
                $Warnings += "- More than one Licensing Group matches the name $($iniFile.LicensingGroups.AllTeachersLicensingGroupName), please check the name. Teacher functions that need Licensing information will not be processed"
                write-host "More than one Licensing Group matches the name $($iniFile.LicensingGroups.AllTeachersLicensingGroupName), please check the name. Teacher functions that need Licensing information will not be processed" -foreground red
                if ($DetailedLoggingEnabled) {
                    Add-Content -Path "$LogFile" -Value "More than one Licensing Group matches the name $($iniFile.LicensingGroups.AllTeachersLicensingGroupName), please check the name. Teacher functions that need Licensing information will not be processed" -Encoding Unicode
                }
            } else {
                $Warnings += "- The Teacher Licensing Group $($iniFile.LicensingGroups.AllTeachersLicensingGroupName) does not exist, Teacher functions that need Licensing information will not be processed"
                write-host "The Teacher Licensing Group $($iniFile.LicensingGroups.AllTeachersLicensingGroupName) does not exist, Teacher functions that need Licensing information will not be processed" -foreground red
                if ($DetailedLoggingEnabled) {
                    Add-Content -Path "$LogFile" -Value "The Teacher Licensing Group $($iniFile.LicensingGroups.AllTeachersLicensingGroupName) does not exist, Teacher functions that need Licensing information will not be processed" -Encoding Unicode
                }
            }
        }    
    }
}



# Apply Student Licensing
$studentlicensingmembers = @()
if ($MajorErrors.Count -eq 0 -and $StudentList.Count -gt 0 -and ($iniFile.LicensingGroups.AssignStudentstoLicensingGroup -eq $true -or $iniFile.LicensingGroups.AssignStudentstoLicensingGroup -eq 1)) {
    write-host "Adding Students to the Licensing Group" -foregroundcolor Green       
    if ($DetailedLoggingEnabled) {
        Add-Content -Path "$LogFile" -Value "`r`nAdding Students to the Licensing Group" -Encoding Unicode
        Add-Content -Path "$LogFile" -Value "======================================`r`n" -Encoding Unicode
        Add-Content -Path "$LogFile" -Value "Search Group Name: $($iniFile.LicensingGroups.AllStudentsLicensingGroupName)`r`n" -Encoding Unicode
    }
    $studentlicensinggroup = $null
	$StudentstoAddLicensing = @()
    $StudentsNotInLicensingGroup = @()
    $AssignmentErrors = @()
    $AssignmentErrorsCaptured=0
    #$studentlicensinggroup = @(Get-MsolGroup -SearchString $iniFile.LicensingGroups.AllStudentsLicensingGroupName)
    if ((Test-IsGuid -ObjectGuid $iniFile.LicensingGroups.AllStudentsLicensingGroupName) -eq $false) {
        $studentlicensinggroup = @(Get-MsolGroup -SearchString $iniFile.LicensingGroups.AllStudentsLicensingGroupName | where {$_.GroupType -eq 'Security' -or $_.GroupType -eq 'MailEnabledSecurity'}) # 1.0.21

        if ($iniFile.LicensingGroups.DisableStudentGroupNameorIDChecking -ne $true -and $iniFile.LicensingGroups.DisableStudentGroupNameorIDChecking -ne 1) {
    	    $studentlicensinggroup = $studentlicensinggroup | where { $_.CommonName -like $iniFile.LicensingGroups.AllStudentsLicensingGroupName -or $_.DisplayName -like $iniFile.LicensingGroups.AllStudentsLicensingGroupName -or $_.EmailAddress -like $iniFile.LicensingGroups.AllStudentsLicensingGroupName } # 1.0.21
        }
    } else {
        $studentlicensinggroup = @(Get-MsolGroup -ObjectID $iniFile.LicensingGroups.AllStudentsLicensingGroupName)
    }

	$GroupCreated=$False
	if ($studentlicensinggroup.Count -eq 0 -or !($studentlicensinggroup)) {
		if ($iniFile.LicensingGroups.CreateAllStudentsLicensingGroupIfNotExists -eq $true -or $iniFile.LicensingGroups.CreateAllStudentsLicensingGroupIfNotExists -eq 1) {
            if ((Test-IsGuid -ObjectGuid $iniFile.LicensingGroups.AllStudentsLicensingGroupName) -eq $false) {
			    if ($MakeChanges -eq $true) {
				    $studentlicensinggroup=New-MsolGroup -DisplayName $iniFile.LicensingGroups.AllStudentsLicensingGroupName -Description "Group for use with Student Licensing - Created by STEPER"
                    if ($studentlicensinggroup) {
                        $GroupCreated=$True
                        if ($DetailedLoggingEnabled) {
    					    Add-Content -Path "$LogFile" -Value "Student Licensing Group $($iniFile.LicensingGroups.AllStudentsLicensingGroupName) was missing and has been created" -Encoding Unicode
				        }
				        $Actions += "- Student Licensing Group $($iniFile.LicensingGroups.AllStudentsLicensingGroupName) was missing and has been created"
				        write-host "Student Licensing Group $($iniFile.LicensingGroups.AllStudentsLicensingGroupName) was missing and has been created" -ForegroundColor yellow
                    } else {
                        if ($DetailedLoggingEnabled) {
    					    Add-Content -Path "$LogFile" -Value "Student Licensing Group $($iniFile.LicensingGroups.AllStudentsLicensingGroupName) was missing but could not be created" -Encoding Unicode
				        }
				        $Warnings += "- Student Licensing Group $($iniFile.LicensingGroups.AllStudentsLicensingGroupName) was missing but could not be created"
				        write-host "Student Licensing Group $($iniFile.LicensingGroups.AllStudentsLicensingGroupName) was missing but could not be created" -ForegroundColor red
                    }
			    } else {
				    if ($DetailedLoggingEnabled) {
					    Add-Content -Path "$LogFile" -Value "Simulation: Student Licensing Group $($iniFile.LicensingGroups.AllStudentsLicensingGroupName) was missing and would have been created" -Encoding Unicode
				    }
				    $Actions += "- Simulation: Student Licensing Group $($iniFile.LicensingGroups.AllStudentsLicensingGroupName) was missing and would have been created"
				    write-host "Simulation: Student Licensing Group $($iniFile.LicensingGroups.AllStudentsLicensingGroupName) was missing and would have been created" -ForegroundColor cyan
                    $GroupCreated=$True
			    }
            } else {
                if ($DetailedLoggingEnabled) {
    			    Add-Content -Path "$LogFile" -Value "Student Licensing Group $($iniFile.LicensingGroups.AllStudentsLicensingGroupName) was missing but could not be created because an ObjectID was provided" -Encoding Unicode
			    }
				$Warnings += "- Student Licensing Group $($iniFile.LicensingGroups.AllStudentsLicensingGroupName) was missing but could not be created because an ObjectID was provided"
				write-host "Student Licensing Group $($iniFile.LicensingGroups.AllStudentsLicensingGroupName) was missing but could not be created because an ObjectID was provided" -ForegroundColor red
            }
		}
	} else {
		if ($studentlicensinggroup.Count -eq 1) {
			$studentlicensingmembers = @(get-msolgroupmember -GroupObjectId $studentlicensinggroup.objectid -All)
		}
	}
		
	if ($studentlicensinggroup.Count -eq 1 -or $GroupCreated -eq $true) {
		if ($DetailedLoggingEnabled) {
			Add-Content -Path "$LogFile" -Value "Resulting Group DisplayName: $($studentlicensinggroup.DisplayName)" -Encoding Unicode
			Add-Content -Path "$LogFile" -Value "Resulting Group ObjectID: $($studentlicensinggroup.objectid)" -Encoding Unicode
		}
        if (($studentlicensingmembers.Count -eq 0) -or !($studentlicensingmembers)) {
            if ($GroupCreated -eq $false) {
				$Warnings += "- There are no Students already in the Licensing Group $($iniFile.LicensingGroups.AllStudentsLicensingGroupName)"
			} else {
				$Information += "- There are no Students already in the newly created Licensing Group $($iniFile.LicensingGroups.AllStudentsLicensingGroupName)"
			}
            $StudentsNotInLicensingGroup = $StudentList # was disabled?
        } else {
            $Actions += "- $($studentlicensingmembers.Count) Students Retrieved from MSOnline that are members of the Licensing Group $($iniFile.LicensingGroups.AllStudentsLicensingGroupName)"
			$StudentsNotInLicensingGroup = @($StudentList | where { $_.ObjectID -notin $studentlicensingmembers.ObjectID }) # 1.0.94
        }
        if ($StudentsNotInLicensingGroup.count -gt 0) {
            $StudentsNotInLicensingGroup = @($StudentsNotInLicensingGroup | Sort EmailAddress)
        }
        $StudentstoAddLicensing = $StudentsNotInLicensingGroup
        if ($iniFile.LicensingGroups.OnlyApplytoUnlicensedStudents -eq $true -or $iniFile.LicensingGroups.OnlyApplytoUnlicensedStudents -eq 1) {
            $StudentstoAddLicensing = @($StudentstoAddLicensing | where { $_.IsLicensed -eq $false })
        }
		if ($StudentstoAddLicensing.Count -gt 0) {
			$string = $StudentstoAddLicensing[0].EmailAddress
			if ($MakeChanges -eq $true) {
    		    if ($DetailedLoggingEnabled) {
				    Process-UserCSVOutput ($StudentstoAddLicensing | Select DisplayName, EmailAddress, GroupMemberType, IsLicensed, LastDirSyncTime, ObjectID, OverallProvisioningStatus, ValidationStatus)
				}
				
				[int]$ResultCount=0
				[int]$personprocessed=0
				$funcstarttime = (Get-Date)
				
				foreach ($Student in $StudentstoAddLicensing) {
                    $e = $error.count

    			    Add-MsolGroupMember -GroupObjectId $studentlicensinggroup.objectid -GroupMemberType User -GroupMemberObjectId $Student.objectid -ErrorAction SilentlyContinue
					$ResultCount+=1
					$personprocessed = $personprocessed + 1					

					if ($error.count -gt $e) {
						$errorstring = ""
						if ($Error[0].Exception.Message) {
							$errorstring = $Error[0].Exception.Message
						} else {
							$errorstring = "(unknown)"
						}
						$AssignmentErrors += [PSCustomObject]@{
						    'DisplayName' = $Student.DisplayName
                            'EmailAddress' = $Student.EmailAddress
                            'UserType' = 'Student'						
                            'ObjectID' = $Student.objectid
                            'IsLicensed' = $Student.IsLicensed
                            'GroupObjectID' = $studentlicensinggroup.objectid
						    'Error' = $errorstring
						}
				        $AssignmentErrorsCaptured+=1				
                        if ($AssignmentErrorsCaptured -le $RepeatLogEventThreshold) {
    						write-host "Failed to add Student to the Licensing Group $($iniFile.LicensingGroups.AllStudentsLicensingGroupName) - $($Student.EmailAddress)" -ForegroundColor Red
                        }
                        if ($AssignmentErrorsCaptured -eq $RepeatLogEventThreshold) {
                            write-host "RepeatLogEventThereshold: Further Student Licensing Group Assignment errors will be supressed" -ForegroundColor yellow
                        }				
					}

					if ($ResultCount -eq $LongJobUpdateInterval) {
						[int]$ResultCount=0

                        $UsersProcessedString, $timestring, $timeremainingseconds = get-timeelapsed -StartTime $funcstarttime -Processed $personprocessed -ToProcess ($StudentstoAddLicensing.Count)

						if ($timeremainingseconds -ge 10) {
							write-host "Adding Students to the Licensing Group - Processed User $UsersProcessedString of $($StudentstoAddLicensing.Count) users - ETR$timestring" -ForegroundColor Gray
							if ($DetailedLoggingEnabled) {
								Add-Content -Path "$LogFile" -Value "Adding Students to the Licensing Group - Processed User $UsersProcessedString of $($StudentstoAddLicensing.Count) users - ETR$timestring" -Encoding Unicode  
							}
						}
					}	
				}

                if ( @($AssignmentErrors).count -gt 0) {
                    if ($DetailedLoggingEnabled) {
				        Add-Content -Path "$LogFile" -Value "`r`nAdd to Licensing Group Errors:" -Encoding Unicode
				        Add-Content -Path "$LogFile" -Value "------------------------------" -Encoding Unicode
				        Process-UserCSVOutput ($AssignmentErrors  | Sort DisplayName, EmailAddress, IsLicensed)
                        Add-Content -Path "$LogFile" -Value "$($AssignmentErrors.count) Add to Licensing Group Errors were encountered during Student License Group Processing" -Encoding Unicode
                    }
				    write-host "$($AssignmentErrors.count) Add to Licensing Group Errors were encountered during Student License Group Processing" -ForegroundColor Red
				    $warnings += "- $($AssignmentErrors.count) Add to Licensing Group Errors were encountered during Student License Group Processing"

			    } else {
				    if ($DetailedLoggingEnabled) {
    			        Add-Content -Path "$LogFile" -Value "$($StudentstoAddLicensing.Count) Students Added to the Licensing Group $($iniFile.LicensingGroups.AllStudentsLicensingGroupName), including $string" -Encoding Unicode
			        }
				    $Actions += "- $($StudentstoAddLicensing.Count) Students Added to the Licensing Group $($iniFile.LicensingGroups.AllStudentsLicensingGroupName), including $string"
                    write-host "$($StudentstoAddLicensing.Count) Students Added to the Licensing Group $($iniFile.LicensingGroups.AllStudentsLicensingGroupName), including $string" -ForegroundColor yellow
                }

			} else {
    			if ($DetailedLoggingEnabled) {
			        Process-UserCSVOutput ($StudentstoAddLicensing | Select DisplayName, EmailAddress, GroupMemberType, IsLicensed, LastDirSyncTime, ObjectID, OverallProvisioningStatus, ValidationStatus)
                    Add-Content -Path "$LogFile" -Value "- Simulation: $($StudentstoAddLicensing.Count) Students would have been added to the Licensing Group $($iniFile.LicensingGroups.AllStudentsLicensingGroupName), including $string" -Encoding Unicode
		        }
			    $Actions += "- Simulation: $($StudentstoAddLicensing.Count) Students would have been added to the Licensing Group $($iniFile.LicensingGroups.AllStudentsLicensingGroupName), including $string"
                write-host "Simulation: $($StudentstoAddLicensing.Count) Students would have been added to the Licensing Group $($iniFile.LicensingGroups.AllStudentsLicensingGroupName), including $string" -ForegroundColor cyan
			}
            $results += [PSCustomObject]@{
                'Type' = 'Student'
				'Quantity' = $($StudentstoAddLicensing.Count)
				'Source' = "$StudentSourceGroupCount StudentSourceGroups"
                'Action' = "$($SimString)Add to Student Licensing Group"
                'Target' = $iniFile.LicensingGroups.AllStudentsLicensingGroupName
                'OtherInfo' = ''
                'AssignmentErrors' = $AssignmentErrors.count
            }
        } else {
    	    $Actions += "- There are no Students to add to the Licensing Group $($iniFile.LicensingGroups.AllStudentsLicensingGroupName)"
            write-host "There are no Students to add to the Licensing Group $($iniFile.LicensingGroups.AllStudentsLicensingGroupName)" -foreground white
            if ($DetailedLoggingEnabled) {
                Add-Content -Path "$LogFile" -Value "There are no Students to add to the Licensing Group $($iniFile.LicensingGroups.AllStudentsLicensingGroupName)" -Encoding Unicode
            }
		}
    } else {
        if ($studentlicensinggroup.Count -gt 1) {
            $Warnings += "- More than one Licensing Group matches the name $($iniFile.LicensingGroups.AllStudentsLicensingGroupName), please check the name. Student Licensing will not be processed"
            write-host "More than one Licensing Group matches the name $($iniFile.LicensingGroups.AllStudentsLicensingGroupName), please check the name. Student Licensing will not be processed" -foreground red
            if ($DetailedLoggingEnabled) {
                Add-Content -Path "$LogFile" -Value "More than one Licensing Group matches the name $($iniFile.LicensingGroups.AllStudentsLicensingGroupName), please check the name. Student Licensing will not be processed" -Encoding Unicode
            }
        } else {
            $Warnings += "- The Student Licensing Group $($iniFile.LicensingGroups.AllStudentsLicensingGroupName) does not exist, Student Licensing will not be processed"
            write-host "The Student Licensing Group $($iniFile.LicensingGroups.AllStudentsLicensingGroupName) does not exist, Student Licensing will not be processed" -foreground red
            if ($DetailedLoggingEnabled) {
                Add-Content -Path "$LogFile" -Value "The Student Licensing Group $($iniFile.LicensingGroups.AllStudentsLicensingGroupName) does not exist, Student Licensing will not be processed" -Encoding Unicode
            }
        }
    }
} else {
# Get Unlicensed Students for Other Functions
    if ($MajorErrors.Count -eq 0 -and $StudentList.Count -gt 0 -and $NeedsStudentLicensingList -eq $true) {
        $studentlicensingmembers = @()
        write-host "Getting the List of Students that are not in the licensing group" -foregroundcolor green  
        if ($DetailedLoggingEnabled) {
            Add-Content -Path "$LogFile" -Value "`r`nGetting the List of Students that are not in the Licensing Group" -Encoding Unicode
            Add-Content -Path "$LogFile" -Value "================================================================`r`n" -Encoding Unicode
            Add-Content -Path "$LogFile" -Value "Search Group Name: $($iniFile.LicensingGroups.AllStudentsLicensingGroupName)`r`n" -Encoding Unicode
        }
        $StudentstoAddLicensing = @()
        $StudentsNotInLicensingGroup = @()
        $studentlicensinggroup=$null
        #$studentlicensinggroup = @(Get-MsolGroup -SearchString $iniFile.LicensingGroups.AllStudentsLicensingGroupName)
        if ((Test-IsGuid -ObjectGuid $iniFile.LicensingGroups.AllStudentsLicensingGroupName) -eq $false) {
            $studentlicensinggroup = @(Get-MsolGroup -SearchString $iniFile.LicensingGroups.AllStudentsLicensingGroupName | where {$_.GroupType -eq 'Security' -or $_.GroupType -eq 'MailEnabledSecurity'}) # 1.0.21
            if ($iniFile.LicensingGroups.DisableStudentGroupNameorIDChecking -ne $true -and $iniFile.LicensingGroups.DisableStudentGroupNameorIDChecking -ne 1) {
	        $studentlicensinggroup = $studentlicensinggroup | where { $_.CommonName -like $iniFile.LicensingGroups.AllStudentsLicensingGroupName -or $_.DisplayName -like $iniFile.LicensingGroups.AllStudentsLicensingGroupName -or $_.EmailAddress -like $iniFile.LicensingGroups.AllStudentsLicensingGroupName } # 1.0.21
            }
        } else {
            $studentlicensinggroup = @(Get-MsolGroup -ObjectID $iniFile.LicensingGroups.AllStudentsLicensingGroupName)
        }

        if ($studentlicensinggroup.Count -eq 1) {
			if ($DetailedLoggingEnabled) {
				Add-Content -Path "$LogFile" -Value "Resulting Group DisplayName: $($studentlicensinggroup.DisplayName)" -Encoding Unicode
				Add-Content -Path "$LogFile" -Value "Resulting Group ObjectID: $($studentlicensinggroup.objectid)" -Encoding Unicode
			}
            $studentlicensingmembers = @(get-msolgroupmember -GroupObjectId $studentlicensinggroup.objectid -All)
            if (($studentlicensingmembers.Count -eq 0) -or !($studentlicensingmembers)) {
                $Warnings += "- There are no Students already in the Licensing Group $($iniFile.LicensingGroups.AllStudentsLicensingGroupName)"
                $StudentsNotInLicensingGroup = $StudentList # was disabled?
            } else {
                $Actions += "- $($studentlicensingmembers.Count) Students Retrieved from MSOnline that are members of the Licensing Group $($iniFile.LicensingGroups.AllStudentsLicensingGroupName)"
				$StudentsNotInLicensingGroup = @($StudentList | where { $_.ObjectID -notin $studentlicensingmembers.ObjectID }) # 1.0.94
            }
            if ($StudentsNotInLicensingGroup.Count -gt 0) {
                $StudentsNotInLicensingGroup = @($StudentsNotInLicensingGroup | Sort EmailAddress)

                $string = $StudentsNotInLicensingGroup[0].EmailAddress
    			if ($DetailedLoggingEnabled) {
			        Process-UserCSVOutput ($StudentsNotInLicensingGroup |Select DisplayName, EmailAddress, GroupMemberType, IsLicensed, LastDirSyncTime, ObjectID, OverallProvisioningStatus, ValidationStatus)
                    Add-Content -Path "$LogFile" -Value "Get List of Students Not in the Licensing Group: $($StudentsNotInLicensingGroup.Count) Students that are not in the Licensing Group $($iniFile.LicensingGroups.AllStudentsLicensingGroupName), including $string" -Encoding Unicode
		        }
			    $Actions += "- Get List of Students Not in the Licensing Group: $($StudentsNotInLicensingGroup.Count) Students that are not in the Licensing Group $($iniFile.LicensingGroups.AllStudentsLicensingGroupName), including $string"



                $results += [PSCustomObject]@{
                    'Type' = 'Teacher'
				    'Quantity' = $($StudentsNotInLicensingGroup.Count)
				    'Source' = "$StudentSourceGroupCount StudentSourceGroups"
                    'Action' = "$($SimString)Get Students not in the licensing group"
                    'Target' = ''
                    'OtherInfo' = ''
                    'AssignmentErrors' = ''
                }
            } else {
                $Actions += "- There are no Students not in the licensing Group $($iniFile.LicensingGroups.AllStudentsLicensingGroupName)"
                write-host "There are no Students not in the licensing Group $($iniFile.LicensingGroups.AllStudentsLicensingGroupName)" -foreground white
                if ($DetailedLoggingEnabled) {
                    Add-Content -Path "$LogFile" -Value "There are no Students not in the licensing Group $($iniFile.LicensingGroups.AllStudentsLicensingGroupName)" -Encoding Unicode
                }
            }
        } else {
            if ($studentlicensinggroup.Count -gt 1) {
                $Warnings += "- More than one Licensing Group matches the name $($iniFile.LicensingGroups.AllStudentsLicensingGroupName), please check the name. Student functions that need Licensing information will not be processed"
                write-host "More than one Licensing Group matches the name $($iniFile.LicensingGroups.AllStudentsLicensingGroupName), please check the name. Student functions that need Licensing information will not be processed" -foreground red
                if ($DetailedLoggingEnabled) {
                    Add-Content -Path "$LogFile" -Value "More than one Licensing Group matches the name $($iniFile.LicensingGroups.AllStudentsLicensingGroupName), please check the name. Student functions that need Licensing information will not be processed" -Encoding Unicode
                }
            } else {
                $Warnings += "- The Student Licensing Group $($iniFile.LicensingGroups.AllStudentsLicensingGroupName) does not exist, Student functions that need Licensing information will not be processed"
                write-host "The Student Licensing Group $($iniFile.LicensingGroups.AllStudentsLicensingGroupName) does not exist, Student functions that need Licensing information will not be processed" -foreground red
                if ($DetailedLoggingEnabled) {
                    Add-Content -Path "$LogFile" -Value "The Student Licensing Group $($iniFile.LicensingGroups.AllStudentsLicensingGroupName) does not exist, Student functions that need Licensing information will not be processed" -Encoding Unicode
                }
            }
        }    
    }
}


# sleep
[int]$SleepSeconds=600 # new default 1.0.32
if ($MajorErrors.Count -eq 0 -and ($StudentstoAddLicensing.Count -gt 0 -or $TeacherstoAddLicensing.Count -gt 0) ) {
    if ($iniFile.Misc.SleepSecondsAfterApplyingLicensing -ne $null) {
        [int]$SleepSeconds = $iniFile.Misc.SleepSecondsAfterApplyingLicensing -as [int]
        if ($SleepSeconds -lt 0) {$SleepSeconds = 600 }
        if ($SleepSeconds -gt 82800) {$SleepSeconds = 600 } # 23 hours
    }
    $SleepSeconds = $SleepSeconds - $SecondsSpentGettingPolicyInformation
    if ( $MakeChanges -eq $true) {
        if ($SleepSeconds -gt 0) {
			[datetime]$timenow = (Get-Date) # -format "dd/MM/yyyy HH:mm:ss")
			$futuredate = $timenow.addseconds($sleepseconds).tostring("dd/MM/yyyy HH:mm:ss")
            write-host "Sleeping for $SleepSeconds seconds after making licensing changes - processing will resume at $futuredate" -foreground green
            if ($DetailedLoggingEnabled) {
                Add-Content -Path "$LogFile" -Value "`r`nSleeping for $SleepSeconds seconds after making licensing changes - processing will resume at $futuredate`r`n" -Encoding Unicode
            }
            Start-Sleep -S $SleepSeconds
			$timenow=$null
        }
		$CurrentlyUnlicensedUsers = @()
		if ($iniFile.Misc.RefreshTeacherLicensingStatusAfterLicensingGroupChanges -eq $true -or $iniFile.Misc.RefreshTeacherLicensingStatusAfterLicensingGroupChanges -eq 1 -or $iniFile.Misc.RefreshStudentLicensingStatusAfterLicensingGroupChanges -eq $true -or $iniFile.Misc.RefreshStudentLicensingStatusAfterLicensingGroupChanges -eq 1) {
			$CurrentlyUnlicensedUsers = Get-MsolUser -All -UnlicensedUsersOnly
		}
		if ($iniFile.Misc.RefreshTeacherLicensingStatusAfterLicensingGroupChanges -eq $true -or $iniFile.Misc.RefreshTeacherLicensingStatusAfterLicensingGroupChanges -eq 1) {
			if ($TeacherstoAddLicensing.Count -gt 0) {
				write-host "Refreshing Teacher Licensing Status for users that have been added to the Teacher licensing group" -foreground green
				if ($DetailedLoggingEnabled) {
					Add-Content -Path "$LogFile" -Value "Refreshing Teacher Licensing Status for users that have been added to the Teacher licensing group" -Encoding Unicode
					Add-Content -Path "$LogFile" -Value "Note: This will impact the use of OnlyApplytoUnlicensedTeachers in the Filtered functions" -Encoding Unicode
					Add-Content -Path "$LogFile" -Value "If you dont want the Teacher licensing status refreshed, set RefreshTeacherLicensingStatusAfterLicensingGroupChanges=False in the settings file`r`n" -Encoding Unicode
				}
				
				[int]$ResultCount=0
				[int]$personprocessed=0
				$funcstarttime = (Get-Date)
				$count = @($TeacherstoAddLicensing | where {$_.IsLicensed -eq $false}).count
				foreach ($User in @($TeacherstoAddLicensing | where {$_.IsLicensed -eq $false})) {
					$ResultCount+=1
					$personprocessed = $personprocessed + 1					
					if ($CurrentlyUnlicensedUsers.ObjectID -notcontains $User.ObjectID) {
						($TeacherList | where {$_.ObjectID -eq $User.ObjectID}).IsLicensed = $true
						$User.IsLicensed = $true
					}
					
					if ($ResultCount -eq $LongJobUpdateInterval) {

						[int]$ResultCount=0

						$UsersProcessedString, $timestring, $timeremainingseconds = get-timeelapsed -StartTime $funcstarttime -Processed $personprocessed -ToProcess ($count)

						if ($timeremainingseconds -ge 10) {
							write-host "Refreshing Teacher Licensing Status for potentially modified users - Processed $UsersProcessedString of $count source users - ETR$timestring" -ForegroundColor Gray
							if ($DetailedLoggingEnabled) {
								Add-Content -Path "$LogFile" -Value "Refreshing Teacher Licensing Status for potentially modified users - Processed $UsersProcessedString of $count source users - ETR$timestring" -Encoding Unicode  
							}
						}
					}
				}
			}
		}
		if ($iniFile.Misc.RefreshStudentLicensingStatusAfterLicensingGroupChanges -eq $true -or $iniFile.Misc.RefreshStudentLicensingStatusAfterLicensingGroupChanges -eq 1) {
			if ($StudentstoAddLicensing.Count -gt 0) {
				write-host "Refreshing Student Licensing Status for users that have been added to the Student licensing group" -foreground green
				if ($DetailedLoggingEnabled) {
					Add-Content -Path "$LogFile" -Value "Refreshing Student Licensing Status for users that have been added to the Student licensing group" -Encoding Unicode
					Add-Content -Path "$LogFile" -Value "Note: This will impact the use of OnlyApplytoUnlicensedStudents in the Filtered functions" -Encoding Unicode
					Add-Content -Path "$LogFile" -Value "If you dont want the Student licensing status refreshed, set RefreshStudentLicensingStatusAfterLicensingGroupChanges=False in the settings file`r`n" -Encoding Unicode					
				}
				[int]$ResultCount=0
				[int]$personprocessed=0
				$funcstarttime = (Get-Date)
				$count = @($StudentstoAddLicensing | where {$_.IsLicensed -eq $false}).count
				foreach ($User in @($StudentstoAddLicensing | where {$_.IsLicensed -eq $false})) {
					$ResultCount+=1
					$personprocessed = $personprocessed + 1
					if ($CurrentlyUnlicensedUsers.ObjectID -notcontains $User.ObjectID) {
						($StudentList | where {$_.ObjectID -eq $User.ObjectID}).IsLicensed = $true
						$User.IsLicensed = $true
					}
					if ($ResultCount -eq $LongJobUpdateInterval) {

						[int]$ResultCount=0

						$UsersProcessedString, $timestring, $timeremainingseconds = get-timeelapsed -StartTime $funcstarttime -Processed $personprocessed -ToProcess ($count)

						if ($timeremainingseconds -ge 10) {
							write-host "Refreshing Student Licensing Status for potentially modified users - Processed $UsersProcessedString of $count source users - ETR$timestring" -ForegroundColor Gray
							if ($DetailedLoggingEnabled) {
								Add-Content -Path "$LogFile" -Value "Refreshing Student Licensing Status for potentially modified users - Processed $UsersProcessedString of $count source users - ETR$timestring" -Encoding Unicode  
							}
						}
					}					
				}
			}
		}	
		
		
    } else {
    if ($SleepSeconds -gt 0) {
            write-host "$($SimString)Would Sleep for $SleepSeconds seconds after making licensing changes" -foreground cyan
            if ($DetailedLoggingEnabled) {
                Add-Content -Path "$LogFile" -Value "`r`n$($SimString)Would Sleep for $SleepSeconds seconds after making licensing changes`r`n" -Encoding Unicode
            }
        }
    }
}


[int]$SecondsSpentGettingPolicyInformation = 0

if ($MajorErrors.Count -eq 0 -and ($iniFile.InformationRetrieval.GetAllUserPolicyInformation -eq $true -or $iniFile.InformationRetrieval.GetAllUserPolicyInformation -eq 1) ) {
    if ($UserFile) {
        $Output = @()

        $OnlineUsers = @()
		#$AzureADUsers = @()
        $FilteredOnlineUsers = @()
		$FilterSource=$null
		$Filter=$null
		$AllUsers = @()
		if ($iniFile.InformationRetrieval.GetUserPackageInformation -eq $true -or $iniFile.InformationRetrieval.GetUserPackageInformation -eq 1) {
			write-host "Information Retrieval - Get All User Policy Information with Package Details" -foregroundcolor green        
			write-host "User Details, Package and Policy Information being saved in $UserFile" -ForegroundColor Yellow
				
			if ($DetailedLoggingEnabled) {
				Add-Content -Path "$LogFile" -Value "`r`nInformation Retrieval - Get All User Policy Information with Package Details" -Encoding Unicode
				Add-Content -Path "$LogFile" -Value "============================================================================`r`n" -Encoding Unicode
				Add-Content -Path "$LogFile" -Value "User Details, Package and Policy Information being saved in $UserFile`r`n" -Encoding Unicode
			}
		} else {
			write-host "Information Retrieval - Get All User Policy Information" -foregroundcolor green        
			write-host "User Details and Policy Information being saved in $UserFile" -ForegroundColor Yellow
				
			if ($DetailedLoggingEnabled) {
				Add-Content -Path "$LogFile" -Value "`r`nInformation Retrieval - Get All User Policy Information" -Encoding Unicode
				Add-Content -Path "$LogFile" -Value "=======================================================`r`n" -Encoding Unicode
				Add-Content -Path "$LogFile" -Value "User Details and Policy Information being saved in $UserFile`r`n" -Encoding Unicode
				Add-Content -Path "$LogFile" -Value "NOTE: To get User Package information, set GetUserPackageInformation=True in the `[InformationRetrieval`] section of the settings file`r`n" -Encoding Unicode
			}			
		}

        $e = $error.count
       # $AllUsers = @(AzureADPreview\Get-AzureADUser -All $true -ErrorAction SilentlyContinue -WarningAction SilentlyContinue)
	    $AllUsers = get-msoluser -All
		$AzureUsers = @()
		$AzureUsers = @(AzureADPreview\Get-AzureADUser -All $true -ErrorAction SilentlyContinue -WarningAction SilentlyContinue)

        if ($error.count -gt $e) {
            $errorstring = ""
			if ($Error[0].Exception.Message) {
				$errorstring = $Error[0].Exception.Message
			} else {
				$errorstring = "(unknown)"
			}
            write-host "Get All User Policy Information: There was an error in retrieving All Users. Processing cannot continue - error message: $errorstring" -foreground red
            if ($DetailedLoggingEnabled) {
                Add-Content -Path "$LogFile" -Value "Get All User Policy Information: There was an error in retrieving All Users. Processing cannot continue - error message: $errorstring" -Encoding Unicode
            }
            $Warnings += "- Get All User Policy Information: There was an error in retrieving All Users. Processing cannot continue - error message: $errorstring"
        } else {
			$e = $error.count
            $OnlineUsers = @(Get-CSOnlineUser -ErrorAction SilentlyContinue -WarningAction SilentlyContinue)
			
		    if ($error.count -gt $e) {
                $errorstring = ""
			    if ($Error[0].Exception.Message) {
				    $errorstring = $Error[0].Exception.Message
			    } else {
				    $errorstring = "(unknown)"
			    }
                write-host "Get All User Policy Information: There was an error in retrieving All CSOnline Users - error message: $errorstring" -foreground red
                if ($DetailedLoggingEnabled) {
                    Add-Content -Path "$LogFile" -Value "Get All User Policy Information: There was an error in retrieving All CSOnline Users - error message: $errorstring" -Encoding Unicode
                }
                $Warnings += "- Get All User Policy Information: There was an error in retrieving All CSOnline Users - error message: $errorstring"
            }
			
			$FilterSource = if ($UseLocalAttributeFiltering -eq $true) { if ($iniFile.InformationRetrieval.FilterSource) { switch($iniFile.InformationRetrieval.FilterSource.Trim($TrimChar)){ "Azure" { "AzureAD" }; "AzureAD" { "AzureAD" }; "AD" { "AzureAD" }; "CS" { "CSOnline" }; "CSO" { "CSOnline" }; "CSOnline" { "CSOnline" }; default { if ($UseAzureADforLocalAttributeFilteringByDefault -eq $true) { "AzureAD" } else { "CSOnline" } }} } else { if ($UseAzureADforLocalAttributeFilteringByDefault -eq $true) { "AzureAD" } else { "CSOnline" } } } else { "CSOnline" }
			
            if ($iniFile.InformationRetrieval.Filter) {
				$FilteringEnabled = $true
                $e = $error.count
                if ($UseLocalAttributeFiltering -eq $false) {
					$FilteredOnlineUsers = @(Get-CSOnlineUser -Filter $iniFile.InformationRetrieval.Filter -ErrorAction SilentlyContinue -WarningAction SilentlyContinue)
					$ObjectField = 'Identity'
				} else {
					#$FilteredOnlineUsers = @(Get-CSOnlineUser -ErrorAction SilentlyContinue -WarningAction SilentlyContinue)
					#$AzureADUsers = @(Get-AzureADUser -all $true -ErrorAction SilentlyContinue -WarningAction SilentlyContinue | where { Invoke-Expression $iniFile.InformationRetrieval.Filter })
					if ($FilterSource -eq 'AzureAD') {
						$FilteredOnlineUsers = @($AzureUsers | where { Invoke-Expression $iniFile.InformationRetrieval.Filter })
						$ObjectField = 'ObjectID'
					} else {
						$FilteredOnlineUsers = @($OnlineUsers | where { Invoke-Expression $iniFile.InformationRetrieval.Filter })
						$ObjectField = 'Identity'
					}
				}
				$Filter = $iniFile.InformationRetrieval.Filter
					

                if ($error.count -gt $e) {
                    $errorstring = ""
			        if ($Error[0].Exception.Message) {
				        $errorstring = $Error[0].Exception.Message
			        } else {
				        $errorstring = "(unknown)"
			        }
					write-host "Get All User Policy Information: There was an error in retrieving Filtered $FilterType $FilterSource Users - error message: $errorstring" -foreground red
					if ($DetailedLoggingEnabled) {
						Add-Content -Path "$LogFile" -Value "Get All User Policy Information: There was an error in retrieving Filtered $FilterType $FilterSource Users - error message: $errorstring" -Encoding Unicode
					}
					$Warnings += "- Get All User Policy Information: There was an error in retrieving Filtered $FilterType $FilterSource Users - error message: $errorstring"
                }
            } else {
				$FilteringEnabled = $false
                $FilteredOnlineUsers = $null
                $Filter = "(none)"
            }

            [int]$ResultCount=0
            [int]$personprocessed=0
			$funcstarttime = (Get-Date)

        
            if (!$iniFile.StudentSourceGroups.DomainFilter) {
                $MatchStudentDomainFilter = "-"
            }


            if (!$iniFile.TeacherSourceGroups.DomainFilter) {
                $MatchTeacherDomainFilter = "-"
            }

            if (!$TestGroupRules.DomainFilter ) {
                $MatchDomainFilter = "-"
            }

            $RequiredRecursiveIDs = @()
            $NotRequiredRecursiveIDs = @()
            $ProblemGroups = @()
            $GroupRulesOK = $true
			$GotGroupUsersOK = $true
            $GroupMatchedUsers = @()
            if ($TestGroupRules.HasGroupRules -eq $true ) {

                write-host "Get All User Policy Information: Building Group Rules Table" -ForegroundColor green
                
                if ($DetailedLoggingEnabled) {
                    Add-Content -Path "$LogFile" -Value "`r`nGet All User Policy Information: Building Group Rules Table" -Encoding Unicode
                    Add-Content -Path "$LogFile" -Value "`r`nGroup Rules Settings"
                    Add-Content -Path "$LogFile" -Value  "--------------------"
                    $TestGroupRules | ft * -AutoSize | Out-String -Width 4096 | Out-File -filepath "$LogFile" -append -Encoding Unicode
                }
				$e = $error.count
                $GroupRulesOK, $ProblemGroups, $RequiredRecursiveIDs, $RequiredORRecursiveIDs, $NotRequiredRecursiveIDs = BuildGroupTable -GroupInformation $TestGroupRules
				if ($GroupRulesOK -eq $true) {
					write-host "Group Rules OK: $GroupRulesOK   Problem Groups: $($ProblemGroups.count)   RequiredGroups: $($RequiredRecursiveIDs.count)   RequiredOrGroups: $($RequiredORRecursiveIDs.count)   NotRequiredGroups: $($NotRequiredRecursiveIDs.count)" -ForegroundColor green
					$GroupMatchedUsers = GetGroupRuleUsers -RequiredRecursiveIDs $RequiredRecursiveIDs -RequiredORRecursiveIDs $RequiredORRecursiveIDs -NotRequiredRecursiveIDs $NotRequiredRecursiveIDs
					write-host "Found $($GroupMatchedUsers.count) users matching the Group Rules" -ForegroundColor green
                } else {
      				write-host "Group Rules OK: $GroupRulesOK   Problem Groups: $($ProblemGroups.count)   RequiredGroups: $($RequiredRecursiveIDs.count)   RequiredOrGroups: $($RequiredORRecursiveIDs.count)   NotRequiredGroups: $($NotRequiredRecursiveIDs.count)" -ForegroundColor yellow
                    $warnings += "- Get All User Policy Information: Group Rules OK: $GroupRulesOK   Problem Groups: $($ProblemGroups.count)   RequiredGroups: $($RequiredRecursiveIDs.count)   RequiredOrGroups: $($RequiredORRecursiveIDs.count)   NotRequiredGroups: $($NotRequiredRecursiveIDs.count)"
                }

                if ($error.count -gt $e) {
                    $errorstring = ""
			        if ($Error[0].Exception.Message) {
				        $errorstring = $Error[0].Exception.Message
			        } else {
				        $errorstring = "(unknown)"
			        }
					write-host "Get All User Policy Information: There was an error in processing Group Rules or matching users - error message: $errorstring" -foreground red
					if ($DetailedLoggingEnabled) {
						Add-Content -Path "$LogFile" -Value "Get All User Policy Information: There was an error in processing Group Rules or matching users - error message: $errorstring" -Encoding Unicode
					}
					$Warnings += "- Get All User Policy Information: There was an error in processing Group Rules or matching users - error message: $errorstring"
					$GotGroupUsersOK = $false
                }

                if ($DetailedLoggingEnabled) {
                    if ($RequiredRecursiveIDs.count -gt 0) {
						Add-Content -Path "$LogFile" -Value "Group Rules: RequiredGroups, Including Recursive"
                        Add-Content -Path "$LogFile" -Value "------------------------------------------------"
                        $RequiredRecursiveIDs | Sort DisplayName,  CommonName, Description, ObjectID | fl * | Out-String -Width 4096 | Out-File -filepath "$LogFile" -append -Encoding Unicode
                    }
                    if ($RequiredORRecursiveIDs.count -gt 0) {
						Add-Content -Path "$LogFile" -Value "Group Rules: RequiredOrGroups, Including Recursive"
                        Add-Content -Path "$LogFile" -Value "--------------------------------------------------"
                        $RequiredORRecursiveIDs | Sort DisplayName,  CommonName, Description, ObjectID | fl * | Out-String -Width 4096 | Out-File -filepath "$LogFile" -append -Encoding Unicode
                    }					
                    if ($NotRequiredRecursiveIDs.count -gt 0) {
						Add-Content -Path "$LogFile" -Value "Group Rules: NotRequiredGroups, Including Recursive"
                        Add-Content -Path "$LogFile" -Value "---------------------------------------------------"
                        $NotRequiredRecursiveIDs | Sort DisplayName,  CommonName, Description, ObjectID | fl * | Out-String -Width 4096 | Out-File -filepath "$LogFile" -append -Encoding Unicode
                    }
                    if ($ProblemGroups.count -gt 0) {
                        Add-Content -Path "$LogFile" -Value "Group Rules: Problem Groups"
                        Add-Content -Path "$LogFile" -Value "---------------------------"
                        $ProblemGroups | Sort DisplayName,  CommonName, Description, ObjectID | fl * | Out-String -Width 4096 | Out-File -filepath "$LogFile" -append -Encoding Unicode
                    }
                    Add-Content -Path "$LogFile" -Value "Get All User Policy Information: Group Rules OK: $GroupRulesOK   Problem Groups: $($ProblemGroups.count)   RequiredGroups: $($RequiredRecursiveIDs.count)   RequiredOrGroups: $($RequiredORRecursiveIDs.count)   NotRequiredGroups: $($NotRequiredRecursiveIDs.count)" -Encoding Unicode
					Add-Content -Path "$LogFile" -Value "Get All User Policy Information: Found $($GroupMatchedUsers.count) users matching the Group Rules" -Encoding Unicode
                }
            }
    # end 1.0.21
	
	
			if ($TestADOURules.HasADOURules -eq $true ) {
					write-host "Active Directory OU/CN Rules - Included OU/CN Or Rules: $($TestADOURules.RequiredOrOUs.count)   Not Required OU/CN Rules: $($TestADOURules.NotRequiredOUs.count)" -ForegroundColor green	
				
				if ($DetailedLoggingEnabled) {
					Add-Content -Path "$LogFile" -Value "`r`nActive Directory OU/CN Rules"
                    Add-Content -Path "$LogFile" -Value "----------------------------"
					$TestADOURules | select -Property * -ExcludeProperty HasADOURules | Sort RequiredOrOUs, NotRequiredOUs | ft * -AutoSize | Out-String -Width 4096 | Out-File -filepath "$LogFile" -append -Encoding Unicode
	
					Add-Content -Path "$LogFile" -Value "Included OU/CN Or Rules: $($TestADOURules.RequiredOrOUs.count)   Not Required OU/CN Rules: $($TestADOURules.NotRequiredOUs.count)"
				}
			}

			$count = $null
			[int]$count = 0
			$string = $null
			$string = "(Not Retrieved - set GetUserPackageInformation=True)"
			
            foreach ($User in $AllUsers) {
				$count++
				$AzureADUser = $AzureUsers | where { $_.ObjectID -eq $User.ObjectID }

                $OnlineUser = @()
                $OnlineUser = $OnlineUsers | where { $_.($ObjectField) -eq  $User.ObjectID } | Select -First 1 # 1.0.84

				if ($iniFile.InformationRetrieval.GetUserPackageInformation -eq $true -or $iniFile.InformationRetrieval.GetUserPackageInformation -eq 1) {
					$string = $null
					$string = (Get-CsUserPolicyPackage -Identity $User.ObjectID).Name # 1.0.84
					if ($string -eq $null) { $string = "(no result)" }
				}

                if ($iniFile.StudentSourceGroups.DomainFilter ) {
                    $MatchStudentDomainFilter = "No"
                    if ($iniFile.StudentSourceGroups.DomainFilter -contains $User.SignInName.Split('@')[1]) {
                        $MatchStudentDomainFilter = "Yes"
                    }
                }

                if ($iniFile.TeacherSourceGroups.DomainFilter) {
                    $MatchTeacherDomainFilter = "No"
                    if ($iniFile.TeacherSourceGroups.DomainFilter -contains $User.SignInName.Split('@')[1]) {
                        $MatchTeacherDomainFilter = "Yes"
                    }
                }
                if ($TestGroupRules.DomainFilter ) {
                    $MatchDomainFilter = "No"
                    if ($TestGroupRules.DomainFilter -contains $User.SignInName.Split('@')[1]) {
                        $MatchDomainFilter = "Yes"
                    }
                }				
				
				#$UserExtension=$null
				#$UserExtension=(AzureADPreview\Get-AzureADUserExtension -objectId $User.Objectid)

				$CN = ""
				$CN = Get-CN -DN ($AzureADUser.ExtensionProperty.onPremisesDistinguishedName)

                $Output += [PSCustomObject]@{
					'#' = $count
                    'DisplayName' = $User.DisplayName
		            'GivenName' = $AzureADUser.GivenName
                    'Surname' = $AzureADUser.Surname
                    'UserPrincipalName' = $User.UserPrincipalName
					'EmailAddress' = $User.SignInName
					'MailNickName' = $AzureADUser.MailNickName
                    'AccountEnabled' = $AzureADUser.AccountEnabled
					'IsLicensed' =  $User.IsLicensed
					'ShowInAddressList' = $AzureADUser.ShowInAddressList
                    'WhenCreated' = $User.Whencreated
					'LastPasswordChangeTimestamp' = $User.LastPasswordChangeTimestamp
					'PasswordPolicies' = $AzureADUser.PasswordPolicies
					'IsCompromised' = $AzureADUser.IsCompromised
                    'CompanyName' = $AzureADUser.CompanyName
					'JobTitle' = $AzureADUser.JobTitle
                    'EmployeeID' = $AzureADUser.ExtensionProperty.employeeId
					'Department' = $AzureADUser.Department
                    'City' = $AzureADUser.City
					'State' = $AzureADUser.State
					'PostalCode' = $AzureADUser.PostalCode
					'UsageLocation' = $AzureADUser.UsageLocation
					'PhysicalDeliveryOfficeName' = $AzureADUser.PhysicalDeliveryOfficeName
					'ObjectID' = $AzureADUser.ObjectID
                    'ObjectType' = $AzureADUser.ObjectType
                    'UserType' = $AzureADUser.UserType
					'DirSyncEnabled' = $AzureADUser.DirSyncEnabled
                    'LastDirSyncTime' = $AzureADUser.LastDirSyncTime
                    'Filter' = $Filter
					'FilterType' = if ($UseLocalAttributeFiltering -eq $true) { "Local" } else { "Remote"}
					'FilterSource' = $FilterSource
                    'IncludedInFilter' = if ($FilteringEnabled -eq $true) { ($FilteredOnlineUsers.($ObjectField) -contains $User.ObjectID.GUID) } else { '-' }
                    'MeetsGroupRules' = if ($TestGroupRules.HasGroupRules -eq $true) { if ($GotGroupUsersOK -eq $true) { if ($GroupRulesOK -eq $true) { if ($GroupMatchedUsers.ObjectID -contains $User.ObjectID.GUID) { "Yes" } else { "No" } } else { "Group Rules Impossible" }  } else { "Error retrieving Group Rules Users"} } else { "-" }
					'ImmutableId' =  $AzureADUser.ImmutableId
					'OnPremGUID' = if ($AzureADUser.ImmutableId) { ([Guid]([Convert]::FromBase64String("$($AzureADUser.ImmutableId)"))).GUID } else { "-" }
					'OnPremDN' = $AzureADUser.ExtensionProperty.onPremisesDistinguishedName
					'AD_OU_CanonicalName_CN' = if ($CN) { $CN } else { "-" }
					'MeetsADOURules' = if ($TestADOURules.HasADOURules -eq $true) { if ($CN) { Check_ADCanonicalName -UserCN $CN -RequiredOrOUs $TestADOURules.RequiredOrOUs -NotRequiredOUs $TestADOURules.NotRequiredOUs } else { "-" } } else { "-" }
					'OnPremisesSecurityIdentifier' = $AzureADUser.OnPremisesSecurityIdentifier
                    'Teacher' = if ($iniFile.TeacherSourceGroupCount.HasGroups -eq $false -or $TeacherList.Count -eq 0) { "(unknown)" } else { ($TeacherList.EmailAddress -contains $User.SigninName) }
                    'NotInTeacherLicensingGroup' =  if ($stafflicensinggroup.count -eq 1) { ( $TeachersNotInLicensingGroup.ObjectID -contains $AzureADUser.ObjectID) } else { "(no group)"}
                    'Student' = if ($iniFile.StudentSourceGroupCount.HasGroups -eq $false -or $StudentList.Count -eq 0) { "(unknown)" } else { ($StudentList.EmailAddress -contains $User.SigninName) }
                    'NotInStudentLicensingGroup' =  if ($studentlicensinggroup.count -eq 1) { ( $StudentsNotInLicensingGroup.ObjectID -contains $AzureADUser.ObjectID) } else { "(no group)"}
                    'OverallProvisioningStatus' = $User.OverallProvisioningStatus
					'SDSObjectType' = $AzureADUser.ExtensionProperty.'extension_fe2174665583431c953114ff7268b7b3_Education_ObjectType'
                    'SDSGrade' =  $AzureADUser.ExtensionProperty.'extension_fe2174665583431c953114ff7268b7b3_Education_Grade'
                    'SDSGraduationYear' = $AzureADUser.ExtensionProperty.'extension_fe2174665583431c953114ff7268b7b3_Education_GraduationYear'
                    #'SDSObjectType' = $UserExtension.extension_fe2174665583431c953114ff7268b7b3_Education_ObjectType
                    #'SDSGrade' =  $UserExtension.extension_fe2174665583431c953114ff7268b7b3_Education_Grade
                    #'SDSGraduationYear' = $UserExtension.extension_fe2174665583431c953114ff7268b7b3_Education_GraduationYear
                    'All Students Domain Filter' = if ($iniFile.StudentSourceGroups.DomainFilter) { "$($iniFile.StudentSourceGroups.DomainFilter)" } else { "(none)" }
                    'User Included in Student Filter' = $MatchStudentDomainFilter
                    'All Teachers Domain Filter' = if ($iniFile.TeacherSourceGroups.DomainFilter) { "$($iniFile.TeacherSourceGroups.DomainFilter)" } else { "(none)" }
                    'User Included in Teacher Filter' = $MatchTeacherDomainFilter
                    'Domain Filter' = if ($TestGroupRules.DomainFilter ) { "$($TestGroupRules.DomainFilter )" } else { "(none)" }
                    'User Included in Domain Filter' = $MatchDomainFilter
                    'TeamsPolicyPackage'= $string
                    'TeamsAppPermissionPolicy' = $OnlineUser.TeamsAppPermissionPolicy.Name
					'TeamsAppSetupPolicy' = $OnlineUser.TeamsAppSetupPolicy.Name
					'TeamsCallingPolicy' = $OnlineUser.TeamsCallingPolicy.Name
					'TeamsChannelsPolicy' = $OnlineUser.TeamsChannelsPolicy.Name
                    'TeamsFeedbackPolicy' = $OnlineUser.TeamsFeedbackPolicy.Name
					'TeamsMeetingPolicy' = $OnlineUser.TeamsMeetingPolicy.Name
					'TeamsMeetingBroadcastPolicy' = $OnlineUser.TeamsMeetingBroadcastPolicy.Name
					'TeamsMessagingPolicy' = $OnlineUser.TeamsMessagingPolicy.Name
                }
           
                $ResultCount+=1
                $personprocessed = $personprocessed + 1
                if ($ResultCount -eq $LongJobUpdateInterval) {
                    [int]$ResultCount=0

                    $UsersProcessedString, $timestring, $timeremainingseconds = get-timeelapsed -StartTime $funcstarttime -Processed $personprocessed -ToProcess ($AllUsers.Count)

                    if ($timeremainingseconds -ge 10) {
                        write-host "Get All User Policy Information - Processed $UsersProcessedString of $($AllUsers.Count) users - ETR$timestring" -ForegroundColor Gray
                        if ($DetailedLoggingEnabled) {
                            Add-Content -Path "$LogFile" -Value "Get All User Policy Information - Processed $UsersProcessedString of $($AllUsers.Count) users - ETR$timestring" -Encoding Unicode  
                        }
                    }
                }
            }
            if ($Output.count -ne $AllUsers.count) {
                $warnings += "- Get All User Policy Information: There was an error processing the user information. Only processed $($Output.count) of $($AllUsers.Count) users out to $UserFile"
                if ($DetailedLoggingEnabled) {
                    Add-Content -Path "$LogFile" -Value "Get All User Policy Information: There was an error processing the user information. Only processed $($Output.count) of $($AllUsers.Count) users out to $UserFile`r`n" -Encoding Unicode  
                }
                write-host "Get All User Policy Information: There was an error processing the user information. Only processed $($Output.count) of $($AllUsers.Count) users out to $UserFile" -ForegroundColor Yellow
            }
        
            $endtime = (Get-Date)
            [int]$timeremainingseconds=(New-TimeSpan -Start $funcstarttime -End $endtime).TotalSeconds
            $SecondsSpentGettingPolicyInformation = $timeremainingseconds

            $timestring=""
			
			if ($timeremainingseconds -ge 86400) {
				$days = [math]::floor($timeremainingseconds/86400)
				$timeremainingseconds = $timeremainingseconds - ($days * 86400)

				if ($days -ne 1) {
					$timestring = " $days days"
				} else {
					$timestring = " $days day"
				}
			}

            if ($timeremainingseconds -ge 3600) {
                $hours = [math]::floor($timeremainingseconds/3600)
                $timeremainingseconds = $timeremainingseconds - ($hours * 3600)

                if ($hours -ne 1) {
					$timestring = "$timestring $hours hrs"
				} else {
					$timestring = "$timestring $hours hr"
				}
            }

			$minutes = [math]::floor($timeremainingseconds/60)
			$timeremainingseconds = $timeremainingseconds - ($minutes * 60)
            if ($minutes -ne 1) {
                $timestring = "$timestring $minutes mins"
            } else {
                $timestring = "$timestring $minutes min"
            }

			if ($timeremainingseconds -ne 1) {
				$timestring = "$timestring $timeremainingseconds secs"
			} else {
				$timestring = "$timestring $timeremainingseconds sec"
			}


            $Output | Export-CSV -Path $UserFile -NoTypeInformation
            write-host "Get All User Policy Information: Processed $($Output.count) of $($AllUsers.Count) user records including Policy Information, in$timestring" -ForegroundColor Green
            $Information += "- Get All User Policy Information: Processed $($Output.count) of $($AllUsers.Count) user records including Policy Information, in$timestring"
            $Actions += "- Get All User Policy Information: Processed $($Output.count) of $($AllUsers.Count) user records including Policy Information, in$timestring"
            if ($DetailedLoggingEnabled) {
                Add-Content -Path "$LogFile" -Value "Get All User Policy Information: Processed $($Output.count) of $($AllUsers.Count) user records including Policy Information, in$timestring`r`n" -Encoding Unicode  
            }
			$AllUsers = $null
        }
    } else {
        $warnings += "- Get All User Policy Information: Unable to Get All User Policy Information - check Logfile Location"
        $Actions += "- Get All User Policy Information: Unable to Get All User Policy Information - check Logfile Location"
        write-host "Get All User Policy Information: Unable to Get All User Policy Information - check Logfile Location" -ForegroundColor Yellow
        if ($DetailedLoggingEnabled) {
            Add-Content -Path "$LogFile" -Value "Get All User Policy Information: Unable to Get All User Policy Information - check Logfile Location`r`n" -Encoding Unicode  

        }
    }
}


# Add Teachers to Unified Group
$TeacherUnifiedMembers = @()
$TeacherUnifiedGroup = @()
$TeacherstoAdd = @()
$AssignmentErrorsCaptured=0
[int]$ApplytoAllTeachersOnNextRunThisFunc=0
[int]$ApplytoAllStudentsOnNextRunThisFunc=0
[int]$TeacherUGOnFailureApplytoAllUsersOnNextRun = (@(if ($iniFile.AllTeachersGroup.OnFailureApplytoAllUsersOnNextRun -ne $null) { if ($iniFile.AllTeachersGroup.OnFailureApplytoAllUsersOnNextRun.Trim($TrimChar) -ne $null -and $iniFile.AllTeachersGroup.OnFailureApplytoAllUsersOnNextRun.Trim($TrimChar) -ne "") { @(iex "echo $($iniFile.AllTeachersGroup.OnFailureApplytoAllUsersOnNextRun.Trim($TrimChar))") | select -Unique | select -first 10 | % { switch($_){0 {[int]-99}; "False" {[int]-99}; 1 {[int]14}; 2 {[int]2}; "A" {[int]2}; "Assignment" {[int]2}; "AssignmentError" {[int]2}; "AssignmentErrors" {[int]2}; "Error" {[int]2}; "Errors" {[int]2}; 4 {[int]4}; "G" {[int]4}; "GroupProblem" {[int]4}; "GroupProblems" {[int]4}; 6 {[int]2,[int]4}; 10 {[int]2,[int]8}; 12 {[int]4,[int]8}; 14 {[int]14}; "All" {[int]14}; "True" {[int]14}; default {[int]0}} } }  else { [int]0 } } else { [int]0 } ) | select -Unique | select -first 10 | Measure-Object -sum).sum | % {if ($_ -gt 14) { 14 } else { if ($_ -lt 0) { 0 } else { $_ } }}
[int]$StudentUGOnFailureApplytoAllUsersOnNextRun = (@(if ($iniFile.AllStudentsGroup.OnFailureApplytoAllUsersOnNextRun -ne $null) { if ($iniFile.AllStudentsGroup.OnFailureApplytoAllUsersOnNextRun.Trim($TrimChar) -ne $null -and $iniFile.AllStudentsGroup.OnFailureApplytoAllUsersOnNextRun.Trim($TrimChar) -ne "") { @(iex "echo $($iniFile.AllStudentsGroup.OnFailureApplytoAllUsersOnNextRun.Trim($TrimChar))") | select -Unique | select -first 10 | % { switch($_){0 {[int]-99}; "False" {[int]-99}; 1 {[int]14}; 2 {[int]2}; "A" {[int]2}; "Assignment" {[int]2}; "AssignmentError" {[int]2}; "AssignmentErrors" {[int]2}; "Error" {[int]2}; "Errors" {[int]2}; 4 {[int]4}; "G" {[int]4}; "GroupProblem" {[int]4}; "GroupProblems" {[int]4}; 6 {[int]2,[int]4}; 10 {[int]2,[int]8}; 12 {[int]4,[int]8}; 14 {[int]14}; "All" {[int]14}; "True" {[int]14}; default {[int]0}} } }  else { [int]0 } } else { [int]0 } ) | select -Unique | select -first 10 | Measure-Object -sum).sum | % {if ($_ -gt 14) { 14 } else { if ($_ -lt 0) { 0 } else { $_ } }}


$GroupCreated=$false
if ($MajorErrors.Count -eq 0 -and $TeacherList.Count -gt 0 -and ($iniFile.AllTeachersGroup.AddAllTeacherstoUnifiedGroup -eq $true -or $iniFile.AllTeachersGroup.AddAllTeacherstoUnifiedGroup -eq 1)) {
    write-host "Adding Teachers to the All Teachers Unified Group" -foregroundcolor Green        
    if ($DetailedLoggingEnabled) {
        Add-Content -Path "$LogFile" -Value "`r`nAdding Teachers to the All Teachers Unified Group" -Encoding Unicode
        Add-Content -Path "$LogFile" -Value "=================================================`r`n" -Encoding Unicode
		Add-Content -Path "$LogFile" -Value "All Teachers Unified Group OnFailureApplytoAllUsersOnNextRun: $TeacherUGOnFailureApplytoAllUsersOnNextRun" -Encoding Unicode
		Add-Content -Path "$LogFile" -Value "Search Group Name: $($iniFile.AllTeachersGroup.AllTeachersUnifiedGroup)" -Encoding Unicode
    }

    $TeacherUnifiedGroup = @(get-UnifiedGroup -Identity $iniFile.AllTeachersGroup.AllTeachersUnifiedGroup -ErrorAction SilentlyContinue -WarningAction SilentlyContinue)

     if ($iniFile.AllTeachersGroup.DisableNameorIDChecking -ne $true -and $iniFile.AllTeachersGroup.DisableNameorIDChecking -ne 1) {

        if ((Test-IsGuid -ObjectGuid $iniFile.AllTeachersGroup.AllTeachersUnifiedGroup) -eq $false) {
	        $TeacherUnifiedGroup = $TeacherUnifiedGroup | where { $_.Name -like $iniFile.AllTeachersGroup.AllTeachersUnifiedGroup -or $_.DisplayName -like $iniFile.AllTeachersGroup.AllTeachersUnifiedGroup -or $_.Alias -like $iniFile.AllTeachersGroup.AllTeachersUnifiedGroup -or $_.PrimarySMTPAddress -like $iniFile.AllTeachersGroup.AllTeachersUnifiedGroup -or $_.Id -like $iniFile.AllTeachersGroup.AllTeachersUnifiedGroup -or $_.ExternalDirectoryObjectID -like $iniFile.AllTeachersGroup.AllTeachersUnifiedGroup -or $_.GUID -like $iniFile.AllTeachersGroup.AllTeachersUnifiedGroup} # 1.0.21
        } else {
            $TeacherUnifiedGroup = $TeacherUnifiedGroup | where { $_.Id -like $iniFile.AllTeachersGroup.AllTeachersUnifiedGroup -or $_.ExternalDirectoryObjectID -like $iniFile.AllTeachersGroup.AllTeachersUnifiedGroup -or $_.GUID -like $iniFile.AllTeachersGroup.AllTeachersUnifiedGroup } # 1.0.22

        }
    }

    if (!$TeacherUnifiedGroup -or $TeacherUnifiedGroup.Count -eq 0) {	
		if ($iniFile.AllTeachersGroup.CreateAllTeachersUnifiedGroupIfNotExists -eq $true -or $iniFile.AllTeachersGroup.CreateAllTeachersUnifiedGroupIfNotExists -eq 1) {
			if ($MakeChanges -eq $true) {
				$TeacherUnifiedGroup = New-unifiedGroup -DisplayName $iniFile.AllTeachersGroup.AllTeachersUnifiedGroup -Alias $iniFile.AllTeachersGroup.AllTeachersUnifiedGroup -accesstype Private -AutoSubscribeNewMembers:$false -Notes "Microsoft Team for All Staff collaboration - Created by STEPER"
                if ($TeacherUnifiedGroup) {
                    $GroupCreated=$True
                    if ($DetailedLoggingEnabled) {
    					Add-Content -Path "$LogFile" -Value "- All Teachers Unified Group $($iniFile.AllTeachersGroup.AllTeachersUnifiedGroup) was missing and has been created" -Encoding Unicode
				    }
				    $Actions += "- All Teachers Unified Group $($iniFile.AllTeachersGroup.AllTeachersUnifiedGroup) was missing and has been created"
				    write-host "All Teachers Unified Group $($iniFile.AllTeachersGroup.AllTeachersUnifiedGroup) was missing and has been created" -ForegroundColor yellow
                } else {
                    if ($DetailedLoggingEnabled) {
    					Add-Content -Path "$LogFile" -Value "- All Teachers Unified Group $($iniFile.AllTeachersGroup.AllTeachersUnifiedGroup) was missing and could not be created" -Encoding Unicode
				    }
				    $Warnings += "- All Teachers Unified Group $($iniFile.AllTeachersGroup.AllTeachersUnifiedGroup) was missing and could not be created"
				    write-host "All Teachers Unified Group $($iniFile.AllTeachersGroup.AllTeachersUnifiedGroup) was missing and could not be created" -ForegroundColor red
					if ( ($TeacherUGOnFailureApplytoAllUsersOnNextRun -band 4) -eq 4) { #1.0.79
						$ApplytoAllTeachersOnNextRunThisFunc = $TeacherstoAdd.Count
					}
                }
			} else {
				if ($DetailedLoggingEnabled) {
					Add-Content -Path "$LogFile" -Value "- Simulation: All Teachers Unified Group $($iniFile.AllTeachersGroup.AllTeachersUnifiedGroup) was missing and would have been created" -Encoding Unicode
				}
				$Actions += "- Simulation: All Teachers Unified Group $($iniFile.AllTeachersGroup.AllTeachersUnifiedGroup) was missing and would have been created"
				write-host "Simulation: All Teachers Unified Group $($iniFile.AllTeachersGroup.AllTeachersUnifiedGroup) was missing and would have been created" -ForegroundColor cyan
                $GroupCreated=$True
			}
		}
	} else {
		$TeacherUnifiedMembers = @(get-UnifiedGroupLinks -Identity $TeacherUnifiedGroup.ExternalDirectoryObjectId -LinkType Members -ErrorAction SilentlyContinue -WarningAction SilentlyContinue)
		if (($TeacherUnifiedMembers.Count -eq 0) -or !($TeacherUnifiedMembers)) {
            $Warnings += "- There are no members in the existing All Teachers Unified Group $($iniFile.AllTeachersGroup.AllTeachersUnifiedGroup)"
		} else {
			$Actions += "- $($TeacherUnifiedMembers.Count) members Retrieved from Exchange Online in the All Teachers Unified Group $($iniFile.AllTeachersGroup.AllTeachersUnifiedGroup)"			
		}
	}
	
    if ($TeacherUnifiedGroup) {
		if ($DetailedLoggingEnabled) {
            Add-Content -Path "$LogFile" -Value "Resulting Group DisplayName: $($TeacherUnifiedGroup.DisplayName)" -Encoding Unicode
			Add-Content -Path "$LogFile" -Value "Resulting Group ExternalDirectoryObjectId: $($TeacherUnifiedGroup.ExternalDirectoryObjectId)" -Encoding Unicode
			Add-Content -Path "$LogFile" -Value "Resulting Group GUID: $($TeacherUnifiedGroup.GUID.GUID)" -Encoding Unicode
        }
		if (($TeacherUnifiedMembers.Count -eq 0) -or !($TeacherUnifiedMembers)) {
            $TeacherstoAdd += $TeacherList
        } else {
            $TeacherstoAdd = @()
			$TeacherstoAdd = @($TeacherList | where { $_.ObjectID -notin $TeacherUnifiedMembers.ExternalDirectoryObjectId }) # 1.0.94
			
        }

        if ($TeacherstoAdd.Count -gt 0) {
			if ($iniFile.AllTeachersGroup.OnlyApplytoUnlicensedTeachers -eq $true -or $iniFile.AllTeachersGroup.OnlyApplytoUnlicensedTeachers -eq 1) {
				$TeacherstoAdd = @($TeacherstoAdd | where { $_.IsLicensed -eq $false })
			}
        }
        if ($TeacherstoAdd.Count -gt 0) {
            $TeacherstoAdd = @($TeacherstoAdd | Sort EmailAddress)
            $string = $TeacherstoAdd[0].EmailAddress
            if ($MakeChanges -eq $true) {
                if ($DetailedLoggingEnabled) {
                    Process-UserCSVOutput ($TeacherstoAdd | Select DisplayName, EmailAddress, GroupMemberType, IsLicensed, LastDirSyncTime, ObjectID, OverallProvisioningStatus, ValidationStatus)
                    Add-Content -Path "$LogFile" -Value "Adding $($TeacherstoAdd.Count) Teachers to the All Teachers Unified Group $($iniFile.AllTeachersGroup.AllTeachersUnifiedGroup), including $string" -Encoding Unicode
                }
                $Actions += "- Adding $($TeacherstoAdd.Count) Teachers to the All Teachers Unified Group $($iniFile.AllTeachersGroup.AllTeachersUnifiedGroup), including $string"
                write-host "Adding $($TeacherstoAdd.Count) Teachers to the All Teachers Unified Group $($iniFile.AllTeachersGroup.AllTeachersUnifiedGroup), including $string" -ForegroundColor yellow
                $e = $error.count
				Add-UnifiedGroupLinks -Identity $TeacherUnifiedGroup.ExternalDirectoryObjectId -LinkType Members -Links $TeacherstoAdd.ObjectId # 1.0.94
                if ($error.count -gt $e) {
                	$errorstring = ""
					if ($Error[0].Exception.Message) {
						$errorstring = $Error[0].Exception.Message
					} else {
						$errorstring = "(unknown)"
					}
                    write-host "An error was detected adding Teachers to the All Teachers Unified Group $($iniFile.AllTeachersGroup.AllTeachersUnifiedGroup) - error message: $errorstring" -foreground red
                    if ($DetailedLoggingEnabled) {
                        Add-Content -Path "$LogFile" -Value "An error was detected adding Teachers to the All Teachers Unified Group $($iniFile.AllTeachersGroup.AllTeachersUnifiedGroup) - error message: $errorstring" -Encoding Unicode
                    }
                    $Warnings += "- An error was detected adding Teachers to the All Teachers Unified Group $($iniFile.AllTeachersGroup.AllTeachersUnifiedGroup) - error message: $errorstring"
					$AssignmentErrorsCaptured = $TeacherstoAdd.Count
					if ( ($TeacherUGOnFailureApplytoAllUsersOnNextRun -band 2) -eq 2) { # 1.0.79
						$ApplytoAllTeachersOnNextRunThisFunc = $TeacherstoAdd.Count
					}
                }
            } else {
                if ($DetailedLoggingEnabled) {
                    Process-UserCSVOutput ($TeacherstoAdd | Select DisplayName, EmailAddress, GroupMemberType, IsLicensed, LastDirSyncTime, ObjectID, OverallProvisioningStatus, ValidationStatus)
                    Add-Content -Path "$LogFile" -Value "- Simulation: $($TeacherstoAdd.Count) Teachers would have been added to the All Teachers Unified Group $($iniFile.AllTeachersGroup.AllTeachersUnifiedGroup), including $string" -Encoding Unicode
                }
                $Actions += "- Simulation: $($TeacherstoAdd.Count) Teachers would have been added to the All Teachers Unified Group $($iniFile.AllTeachersGroup.AllTeachersUnifiedGroup), including $string"
                write-host "Simulation: $($TeacherstoAdd.Count) Teachers would have been added to the All Teachers Unified Group $($iniFile.AllTeachersGroup.AllTeachersUnifiedGroup), including $string" -ForegroundColor cyan
            }
            $results += [PSCustomObject]@{
                'Type' = 'Teacher'
				'Quantity' = $($TeacherstoAdd.Count)
				'Source' = "$TeacherSourceGroupCount TeacherSourceGroups"
                'Action' = "$($SimString)Add to Teacher Unified Group"
                'Target' = $iniFile.AllTeachersGroup.AllTeachersUnifiedGroup
                'OtherInfo' = ''
                'AssignmentErrors' = $AssignmentErrorsCaptured
            }
        } else {
            $Actions += "- There are no Teachers to add to the All Teachers Unified Group $($iniFile.AllTeachersGroup.AllTeachersUnifiedGroup)"
            write-host "There are no Teachers to add to the All Teachers Unified Group $($iniFile.AllTeachersGroup.AllTeachersUnifiedGroup)" -foreground white
            if ($DetailedLoggingEnabled) {
                Add-Content -Path "$LogFile" -Value "There are no Teachers to add to the All Teachers Unified Group $($iniFile.AllTeachersGroup.AllTeachersUnifiedGroup)" -Encoding Unicode
            }
        }
    } else {
    $Warnings += "- The All Teachers Unified Group $($iniFile.AllTeachersGroup.AllTeachersUnifiedGroup) does not exist, All Teachers Unified Group will not be processed"
	if ( ($TeacherUGOnFailureApplytoAllUsersOnNextRun -band 4) -eq 4) { # 1.0.79
		$ApplytoAllTeachersOnNextRunThisFunc = $TeacherstoAdd.Count
	}
    write-host "The All Teachers Unified Group $($iniFile.AllTeachersGroup.AllTeachersUnifiedGroup) does not exist, All Teachers Unified Group will not be processed" -foreground red
    if ($DetailedLoggingEnabled) {
        Add-Content -Path "$LogFile" -Value "The All Teachers Unified Group $($iniFile.AllTeachersGroup.AllTeachersUnifiedGroup) does not exist, All Teachers Unified Group will not be processed" -Encoding Unicode
        }
    }
}


# Add Students to Unified Group
$StudentUnifiedMembers = @()
$StudentUnifiedGroup = @()
$StudentstoAdd = @()
$AssignmentErrorsCaptured=0

$GroupCreated=$false
if ($MajorErrors.Count -eq 0 -and $StudentList.Count -gt 0 -and ($iniFile.AllStudentsGroup.AddAllStudentstoUnifiedGroup -eq $true -or $iniFile.AllStudentsGroup.AddAllStudentstoUnifiedGroup -eq 1)) {
    write-host "Adding Students to the All Students Unified Group" -foregroundcolor Green       
    if ($DetailedLoggingEnabled) {
        Add-Content -Path "$LogFile" -Value "`r`nAdding Students to the All Students Unified Group" -Encoding Unicode
        Add-Content -Path "$LogFile" -Value "=================================================`r`n" -Encoding Unicode
		Add-Content -Path "$LogFile" -Value "All Students Unified Group OnFailureApplytoAllUsersOnNextRun: $StudentUGOnFailureApplytoAllUsersOnNextRun" -Encoding Unicode
		Add-Content -Path "$LogFile" -Value "Search Group Name: $($iniFile.AllTeachersGroup.AllTeachersUnifiedGroup)" -Encoding Unicode
    }
    $StudentUnifiedGroup = @(get-UnifiedGroup -Identity $iniFile.AllStudentsGroup.AllStudentsUnifiedGroup -ErrorAction SilentlyContinue -WarningAction SilentlyContinue)
	
    if ($iniFile.AllStudentsGroup.DisableNameorIDChecking -ne $true -and $iniFile.AllStudentsGroup.DisableNameorIDChecking -ne 1) {
	
        if ((Test-IsGuid -ObjectGuid $iniFile.AllTeachersGroup.AllTeachersUnifiedGroup) -eq $false) {
            $StudentUnifiedGroup = $StudentUnifiedGroup | where { $_.Name -like $iniFile.AllStudentsGroup.AllStudentsUnifiedGroup -or $_.DisplayName -like $iniFile.AllStudentsGroup.AllStudentsUnifiedGroup -or $_.Alias -like $iniFile.AllStudentsGroup.AllStudentsUnifiedGroup -or $_.PrimarySMTPAddress -like $iniFile.AllTeachersGroup.AllTeachersUnifiedGroup -or $_.Id -like $iniFile.AllStudentsGroup.AllStudentsUnifiedGroup -or $_.ExternalDirectoryObjectID -like $iniFile.AllStudentsGroup.AllStudentsUnifiedGroup -or $_.GUID -like $iniFile.AllStudentsGroup.AllStudentsUnifiedGroup} # 1.0.21
        } else {
            $StudentUnifiedGroup = $StudentUnifiedGroup | where { $_.Id -like $iniFile.AllStudentsGroup.AllStudentsUnifiedGroup -or $_.ExternalDirectoryObjectID -like $iniFile.AllStudentsGroup.AllStudentsUnifiedGroup -or $_.GUID -like $iniFile.AllStudentsGroup.AllStudentsUnifiedGroup } # 1.0.22
        }
    }

	if (!$StudentUnifiedGroup -or $StudentUnifiedGroup.Count -eq 0) {	
		if ($iniFile.AllStudentsGroup.CreateAllStudentsUnifiedGroupIfNotExists -eq $true -or $iniFile.AllStudentsGroup.CreateAllStudentsUnifiedGroupIfNotExists -eq 1) {
			if ($MakeChanges -eq $true) {
				$StudentUnifiedGroup = New-unifiedGroup -DisplayName $iniFile.AllStudentsGroup.AllStudentsUnifiedGroup -Alias $iniFile.AllStudentsGroup.AllStudentsUnifiedGroup -accesstype Private -AutoSubscribeNewMembers:$false -Notes "Created by STEPER"
                if ($StudentUnifiedGroup) {
                    $GroupCreated=$True
                    if ($DetailedLoggingEnabled) {
    					Add-Content -Path "$LogFile" -Value "- All Students Unified Group $($iniFile.AllStudentsGroup.AllStudentsUnifiedGroup) was missing and has been created" -Encoding Unicode
				    }
				    $Actions += "- All Students Unified Group $($iniFile.AllStudentsGroup.AllStudentsUnifiedGroup) was missing and has been created"
				    write-host "All Students Unified Group $($iniFile.AllStudentsGroup.AllStudentsUnifiedGroup) was missing and has been created" -ForegroundColor yellow
                } else {
                    if ($DetailedLoggingEnabled) {
    					Add-Content -Path "$LogFile" -Value "- All Students Unified Group $($iniFile.AllStudentsGroup.AllStudentsUnifiedGroup) was missing and could not be created" -Encoding Unicode
				    }
				    $Warnings += "- All Students Unified Group $($iniFile.AllStudentsGroup.AllStudentsUnifiedGroup) was missing and could not be created"
				    write-host "All Students Unified Group $($iniFile.AllStudentsGroup.AllStudentsUnifiedGroup) was missing and could not be created" -ForegroundColor red
					if ( ($StudentUGOnFailureApplytoAllUsersOnNextRun -band 4) -eq 4) { # 1.0.79
						$ApplytoAllStudentsOnNextRunThisFunc = $StudentstoAdd.Count
					}
                }
			} else {
				if ($DetailedLoggingEnabled) {
					Add-Content -Path "$LogFile" -Value "- Simulation: All Students Unified Group $($iniFile.AllStudentsGroup.AllStudentsUnifiedGroup) was missing and would have been created" -Encoding Unicode
				}
				$Actions += "- Simulation: All Students Unified Group $($iniFile.AllStudentsGroup.AllStudentsUnifiedGroup) was missing and would have been created"
				write-host "Simulation: All Students Unified Group $($iniFile.AllStudentsGroup.AllStudentsUnifiedGroup) was missing and would have been created" -ForegroundColor cyan
                $GroupCreated=$True
			}
		}
	} else {
        $StudentUnifiedMembers = @(get-UnifiedGroupLinks -Identity $StudentUnifiedGroup.ExternalDirectoryObjectId -LinkType Members -ErrorAction SilentlyContinue -WarningAction SilentlyContinue)
         if (($StudentUnifiedMembers.Count -eq 0) -or !($StudentUnifiedMembers)) {
            $Warnings += "- There are no members in the existing All Students Unified Group $($iniFile.AllStudentsGroup.AllStudentsUnifiedGroup)"
        } else {
            $Actions += "- $($StudentUnifiedMembers.Count) members Retrieved from Exchange Online in the All Students Unified Group $($iniFile.AllStudentsGroup.AllStudentsUnifiedGroup)"
        }
	}
	
    if ($StudentUnifiedGroup) {
		if ($DetailedLoggingEnabled) {
            Add-Content -Path "$LogFile" -Value "Resulting Group DisplayName: $($StudentUnifiedGroup.DisplayName)" -Encoding Unicode
			Add-Content -Path "$LogFile" -Value "Resulting Group ExternalDirectoryObjectId: $($StudentUnifiedGroup.ExternalDirectoryObjectId)" -Encoding Unicode
			Add-Content -Path "$LogFile" -Value "Resulting Group GUID: $($StudentUnifiedGroup.GUID.GUID)" -Encoding Unicode
        }
		if (($StudentUnifiedMembers.Count -eq 0) -or !($StudentUnifiedMembers)) {
            $StudentstoAdd += $StudentList
        } else {
			$StudentstoAdd = @()
			$StudentstoAdd = @($StudentList | where { $_.ObjectID -notin $StudentUnifiedMembers.ExternalDirectoryObjectId }) # 1.0.94        
		}
		

        if ($StudentstoAdd.Count -gt 0) {
                if ($iniFile.AllStudentsGroup.OnlyApplytoUnlicensedStudents -eq $true -or $iniFile.AllStudentsGroup.OnlyApplytoUnlicensedStudents -eq 1) {
                    $StudentstoAdd = @($StudentstoAdd | where { $_.IsLicensed -eq $false })
                }
        }

        if ($StudentstoAdd.Count -gt 0) {
            $StudentstoAdd = @($StudentstoAdd | Sort EmailAddress)
            $string = $StudentstoAdd[0].EmailAddress
            if ($MakeChanges -eq $true) {
                if ($DetailedLoggingEnabled) {
                    Process-UserCSVOutput ($StudentstoAdd | Select DisplayName, EmailAddress, GroupMemberType, IsLicensed, LastDirSyncTime, ObjectID, OverallProvisioningStatus, ValidationStatus)
                    Add-Content -Path "$LogFile" -Value "Adding $($StudentstoAdd.Count) Students to the All Students Unified Group $($iniFile.AllStudentsGroup.AllStudentsUnifiedGroup), including $string" -Encoding Unicode
                }
                $Actions += "- Adding $($StudentstoAdd.Count) Students to the All Students Unified Group $($iniFile.AllStudentsGroup.AllStudentsUnifiedGroup), including $string"
                write-host "Adding $($StudentstoAdd.Count) Students to the All Students Unified Group $($iniFile.AllStudentsGroup.AllStudentsUnifiedGroup), including $string" -ForegroundColor yellow
                $e = $error.count
				Add-UnifiedGroupLinks -Identity $StudentUnifiedGroup.ExternalDirectoryObjectId -LinkType Members -Links $StudentstoAdd.ObjectId # 1.0.94
                if ($error.count -gt $e) {
                	$errorstring = ""
					if ($Error[0].Exception.Message) {
						$errorstring = $Error[0].Exception.Message
					} else {
						$errorstring = "(unknown)"
					}
                    write-host "An error was detected adding Students to the All Students Unified Group $($iniFile.AllStudentsGroup.AllStudentsUnifiedGroup) - error message: $errorstring" -foreground red
                    if ($DetailedLoggingEnabled) {
                        Add-Content -Path "$LogFile" -Value "An error was detected adding Students to the All Students Unified Group $($iniFile.AllStudentsGroup.AllStudentsUnifiedGroup) - error message: $errorstring" -Encoding Unicode
                    }
                    $Warnings += "- An error was detected adding Students to the All Students Unified Group $($iniFile.AllStudentsGroup.AllStudentsUnifiedGroup) - error message: $errorstring"
					$AssignmentErrorsCaptured = $StudentstoAdd.Count
					if ( ($StudentUGOnFailureApplytoAllUsersOnNextRun -band 2) -eq 2) { # 1.0.79
						$ApplytoAllStudentsOnNextRunThisFunc = $StudentstoAdd.Count
					}
                }  
             } else {
                if ($DetailedLoggingEnabled) {
                    Process-UserCSVOutput ($StudentstoAdd | Select DisplayName, EmailAddress, GroupMemberType, IsLicensed, LastDirSyncTime, ObjectID, OverallProvisioningStatus, ValidationStatus)
                    Add-Content -Path "$LogFile" -Value "- Simulation: $($StudentstoAdd.Count) Students would have been added to the All Students Unified Group $($iniFile.AllStudentsGroup.AllStudentsUnifiedGroup), including $string" -Encoding Unicode
                }
                $Actions += "- Simulation: $($StudentstoAdd.Count) Students would have been added to the All Students Unified Group $($iniFile.AllStudentsGroup.AllStudentsUnifiedGroup), including $string"
                write-host "Simulation: $($StudentstoAdd.Count) Students would have been added to the All Students Unified Group $($iniFile.AllStudentsGroup.AllStudentsUnifiedGroup), including $string" -ForegroundColor cyan
            }
            $results += [PSCustomObject]@{
                'Type' = 'Student'
				'Quantity' = $($StudentstoAdd.Count)
				'Source' = "$StudentSourceGroupCount StudentSourceGroups"
                'Action' = "$($SimString)Add to Student Unified Group"
                'Target' = $iniFile.AllStudentsGroup.AllStudentsUnifiedGroup
                'OtherInfo' = ''
                'AssignmentErrors' = $AssignmentErrorsCaptured
            }
        } else {
            $Actions += "- There are no Students to add to the All Students Unified Group $($iniFile.AllStudentsGroup.AllStudentsUnifiedGroup)"
            write-host "There are no Students to add to the All Students Unified Group $($iniFile.AllStudentsGroup.AllStudentsUnifiedGroup)" -foreground white
            if ($DetailedLoggingEnabled) {
                Add-Content -Path "$LogFile" -Value "There are no Students to add to the All Students Unified Group $($iniFile.AllStudentsGroup.AllStudentsUnifiedGroup)" -Encoding Unicode
            }
        }
    } else {
    $Warnings += "- The All Students Unified Group $($iniFile.AllStudentsGroup.AllStudentsUnifiedGroup) does not exist, All Students Unified Group will not be processed"
	if ( ($StudentUGOnFailureApplytoAllUsersOnNextRun -band 4) -eq 4) { # 1.0.79
		$ApplytoAllStudentsOnNextRunThisFunc = $StudentstoAdd.Count
	}
    write-host "The All Students Unified Group $($iniFile.AllStudentsGroup.AllStudentsUnifiedGroup) does not exist, All Students Unified Group will not be processed" -foreground red
    if ($DetailedLoggingEnabled) {
        Add-Content -Path "$LogFile" -Value "The All Students Unified Group $($iniFile.AllStudentsGroup.AllStudentsUnifiedGroup) does not exist, All Students Unified Group will not be processed" -Encoding Unicode
        }
    }
}

if ($ApplytoAllTeachersOnNextRunThisFunc -gt 0 -or $ApplytoAllStudentsOnNextRunThisFunc -gt 0) {
	$ApplytoAllonNextRunSummary += [PSCustomObject]@{
		'Function' = 'All Teachers Group and All Students Group'
		'Teachers' = $ApplytoAllTeachersOnNextRunThisFunc
		'Students' = $ApplytoAllStudentsOnNextRunThisFunc
	}
	if ($ApplytoAllTeachersOnNextRunThisFunc -gt 0) { $ApplytoAllTeachersOnNextRun=$true }
	if ($ApplytoAllStudentsOnNextRunThisFunc -gt 0) { $ApplytoAllStudentsOnNextRun=$true }		
}



if ($MajorErrors.Count -eq 0 -and ($ApplytoAllTeachers -eq $true -or $ApplytoAllStudents -eq $true)) {
    if ($DetailedLoggingEnabled) {	
        Add-Content -Path "$LogFile" -Value "`r`n`r`nOverrides" -Encoding Unicode
        Add-Content -Path "$LogFile" -Value "=========`r`n" -Encoding Unicode		
		if ($ApplytoAllTeachers -eq $true) {
			Add-Content -Path "$LogFile" -Value "Apply to All Teachers: $ApplytoAllTeachers"
		}
		if ($ApplytoAllStudents -eq $true) {
			Add-Content -Path "$LogFile" -Value "Apply to All Students: $ApplytoAllStudents"
		}
		if ($ApplytoAllStudents -eq $true) {
			Add-Content -Path "$LogFile" -Value "(This affects setting the Student Department, Filtered Security Groups, Unified Groups, Distribution Groups, Teams Packages and Teams Policies)`r`n"
		} else {
			Add-Content -Path "$LogFile" -Value "(This affects setting Filtered Security Groups, Unified Groups, Distribution Groups, Teams Packages and Teams Policies)`r`n"
		}
	}
}

# Need to be connected to Azure AD - this is already done above

# Set Student Department
$studentdepartmentchanges = @()
if ($MajorErrors.Count -eq 0 -and $StudentList.Count -gt 0 -and ($iniFile.Department.SetStudentDepartment -eq $true -or $iniFile.Department.SetStudentDepartment -eq 1)) {
    write-host "Setting Student Department" -foregroundcolor Green
    if ($DetailedLoggingEnabled) {
        Add-Content -Path "$LogFile" -Value "`r`nSetting Student Department" -Encoding Unicode
        Add-Content -Path "$LogFile" -Value "==========================`r`n" -Encoding Unicode
		Add-Content -Path "$LogFile" -Value "Set Student Department OnFailureApplytoAllUsersOnNextRun: $($iniFile.Department.OnFailureApplytoAllUsersOnNextRun)" -Encoding Unicode
    }
    $StudentDepartmentList = @()
    if ($iniFile.Department.SetStudentDepartmentToAllStudents -eq $true -or $iniFile.Department.SetStudentDepartmentToAllStudents -eq 1 -or $ApplytoAllStudents -eq $true) { # use the entire list
        $StudentDepartmentList = $StudentList
    } else {
        $StudentDepartmentList = $StudentsNotInLicensingGroup # this will always be the same or smaller number of students that $StudentList
    }

    if ($iniFile.Department.OnlyApplytoUnlicensedStudents -eq $true -or $iniFile.Department.OnlyApplytoUnlicensedStudents -eq 1) {
        $StudentDepartmentList = @($StudentDepartmentList | where { $_.IsLicensed -eq $false })
    }

    if ($iniFile.Department.UseGraduationYear -eq $true -or $iniFile.Department.UseGraduationYear -eq 1) {
        $stringd="Graduation Year"
        $BasedonGrade=$false
    } else {
        $stringd="Grade"
        $BasedonGrade=$true
    }


#    $StudentDepartmentList  = $StudentDepartmentList | Sort EmailAddress
    if ($StudentDepartmentList.Count -gt 0) {
        [int]$AzureADproblem = 0
        [int]$FilteredNonBlank = 0
        $Output = @()
        
        [int]$ResultCount=0
        [int]$UsersProcessed=0
        [int]$ChangesMade=0
        $funcstarttime = (Get-Date)
        foreach ($Student in $StudentDepartmentList) {
            $ResultCount+=1
            $UsersProcessed+=1
            $ADUser = AzureADPreview\Get-AzureADUser -ObjectId $Student.ObjectID
            #$OldUser = AzureADPreview\Get-AzureADUser -Filter "UserPrincipalName eq '$($Student.EmailAddress)'"
            if ( $ADUser) {
                $skipped=$false
                if ($iniFile.Department.SetStudentDepartmentOnlyIfDepartmentBlank -eq $true -or $iniFile.Department.SetStudentDepartmentOnlyIfDepartmentBlank -eq 1) { # use the entire list
                    if ($ADUser.Department -ne $null) {
                        $skipped=$true
                    }
                }
                if ($skipped -eq $true) {
                    $FilteredNonBlank = $FilteredNonBlank + 1
                    $Output += [PSCustomObject]@{
                        'DisplayName' = $Student.DisplayName
                        'EmailAddress' = $Student.EmailAddress
                        'GroupMemberType' = $Student.GroupMemberType
                        'IsLicensed' = $Student.IsLicensed
                        'Department' = $ADUser.Department
                        'NewDepartment' = $ADUser.Department
                        'Status' = 'Unchanged due to Department field not being blank'
                        'ObjectID' = $Student.ObjectID
                        'ErrorGenerated' = $false
                        'ErrorMessage' = ""
                        }
                } else {
                    $ChangesMade +=1
                    if ($MakeChanges -eq $true) {
                        if ($BasedonGrade -eq $false) {
                            $YearGroup = (AzureADPreview\Get-AzureADUserExtension -objectId $Student.objectid).extension_fe2174665583431c953114ff7268b7b3_Education_GraduationYear
                        } else {
                            $YearGroup = (AzureADPreview\Get-AzureADUserExtension -objectId $Student.objectid).extension_fe2174665583431c953114ff7268b7b3_Education_Grade
                        }
                        
                        #$YearGroup = (AzureADPreview\Get-AzureADUserExtension -objectId $Student.objectid).extension_fe2174665583431c953114ff7268b7b3_Education_ObjectType
                        
                        $ErrorGenerated=$false
                        $errorstring = ""
                        $e = $error.count
                        AzureADPreview\Set-AzureADUser -ObjectId $pupil.objectid -Department $YearGroup

                        if ($error.count -gt $e) {
							if ($Error[0].Exception.Message) {
								$errorstring = $Error[0].Exception.Message
							} else {
								$errorstring = "(unknown)"
							}
                        $ErrorGenerated=$true
                        }


                        $Output += [PSCustomObject]@{
                            'DisplayName' = $Student.DisplayName
                            'EmailAddress' = $Student.EmailAddress
                            'GroupMemberType' = $Student.GroupMemberType
                            'IsLicensed' = $Student.IsLicensed
                            'Department' = $ADUser.Department
                            'NewDepartment' = $YearGroup
                            'Status' = 'Updated'
                            'ObjectID' = $Student.ObjectID
                            'ErrorGenerated' = $ErrorGenerated
                            'ErrorMessage' = $errorstring
                        }
                    } else {
                        $Output += [PSCustomObject]@{
                            'DisplayName' = $Student.DisplayName
                            'EmailAddress' = $Student.EmailAddress
                            'GroupMemberType' = $Student.GroupMemberType
                            'IsLicensed' = $Student.IsLicensed
                            'Department' = $ADUser.Department
                            'NewDepartment' = $ADUser.Department
                            'Status' = 'Simulation: No changes made'
                            'ObjectID' = $Student.ObjectID
                            'ErrorGenerated' = $false
                            'ErrorMessage' = ""
                        }
                    }
                }
            } else {
            $AzureADproblem = $AzureADproblem +1
            $Output += [PSCustomObject]@{
                'DisplayName' = $Student.DisplayName
                'EmailAddress' = $Student.EmailAddress
                'GroupMemberType' = $Student.GroupMemberType
                'IsLicensed' = $Student.IsLicensed
                'OldDepartment' = ''
                'Department' = ''
                'Status' = 'Missing in Azure AD'
                'ObjectID' = $Student.ObjectID
                'ErrorGenerated' = $false
                'ErrorMessage' = ""
                }
            }
            if ($ResultCount -eq $LongJobUpdateInterval) {
                [int]$ResultCount=0

                $UsersProcessedString, $timestring, $timeremainingseconds = get-timeelapsed -StartTime $funcstarttime -Processed $UsersProcessed -ToProcess ($StudentDepartmentList.Count)

                if ($timeremainingseconds -ge 10) {
                    write-host "$($simstring)Setting Student Department based on $stringd - Processed $UsersProcessedString of $($StudentDepartmentList.Count) users - ETR$timestring" -ForegroundColor Gray
                    
                    if ($DetailedLoggingEnabled) {
                        Add-Content -Path "$LogFile" -Value "$($simstring)Setting Student Department based on $stringd - Processed $UsersProcessedString of $($StudentDepartmentList.Count) users - ETR$timestring" -Encoding Unicode  
                    }
                }
            }
        } #end for loop

        $string = '(none)'
        $obj = $Output | where { $_.Status -ne 'Missing in Azure AD' -and $_.Status -ne 'Unchanged due to Department field not being blank' } | Select -First 1
        if ($obj) {
            $string = $obj.EmailAddress
        }

        if ($iniFile.Department.SetStudentDepartmentOnlyIfDepartmentBlank -eq $true -or $iniFile.Department.SetStudentDepartmentOnlyIfDepartmentBlank -eq 1) {
            $FilterString = "Only those with an empty department field"
            $FilterStringB = " and having no existing department setting"
        } else {
            [string]$FilterString = ""
            [string]$FilterStringB = ""
        }
        if ($AzureADproblem -gt 0) {
            $StringC = "($AzureADproblem students not changed due to an AzureAD problem)"
            $Warnings += "- $AzureADproblem students were not matched in AzureAD against M365"
            write-host "$AzureADproblem students were not matched in AzureAD against M365" -foreground red
        } else {
            [string]$StringC = ""
        }

        if ($DetailedLoggingEnabled) {
			Process-UserCSVOutput ($Output)
            if ($MakeChanges -eq $true) {
                Add-Content -Path "$LogFile" -Value "- $($StudentDepartmentList.Count) Students processed with $ChangesMade changes made, setting the Department based on $stringd,$FilterStringB including $string $StringC" -Encoding Unicode
            } else {
                Add-Content -Path "$LogFile" -Value "- Simulation: $($StudentDepartmentList.Count) Students processed with $ChangesMade potential changes, to simulate setting the Department based on $stringd,$FilterStringB including $string $StringC" -Encoding Unicode
            }
		}
        $TotalErrors = 0
        if ($MakeChanges -eq $true) {

            $TotalErrors = ($Output | where { $_.ErrorGenerated -eq $true}).Count

            if ( $TotalErrors -gt 0) {
                if ($DetailedLoggingEnabled) {
				    Add-Content -Path "$LogFile" -Value "`r`nSet Department Errors:" -Encoding Unicode
				    Add-Content -Path "$LogFile" -Value "----------------------`r`n" -Encoding Unicode
				    
                    Add-Content -Path "$LogFile" -Value "$TotalErrors Set Department Errors were encountered during Set Department Processing" -Encoding Unicode
                }
				write-host "$TotalErrors Set Department Errors were encountered during Set Department Processing" -ForegroundColor Red
				$warnings += "- $TotalErrors Set Department Errors were encountered during Set Department Processing"
			} else {
                write-host "$($Output.Count) Students processed with $ChangesMade changes made, setting the Department based on $stringd,$FilterStringB including $string $StringC" -ForegroundColor yellow
                $Actions += "- $($Output.Count) Students processed with $ChangesMade changes made, setting the Department based on $stringd,$FilterStringB including $string $StringC"
            }
        } else {
            write-host "Simulation: $($Output.Count) Students processed with $ChangesMade potential changes, to simulate setting the Department based on $stringd,$FilterStringB including $string $StringC" -ForegroundColor cyan
            $Actions += "- Simulation: $($Output.Count) Students processed with $ChangesMade potential changes, to simulate setting the Department based on $stringd,$FilterStringB including $string $StringC"
        }



        if ($iniFile.Department.SetStudentDepartmentToAllStudents -eq $true -or $iniFile.Department.SetStudentDepartmentToAllStudents -eq 1) { 
            $results += [PSCustomObject]@{
                'Type' = 'Student'
				#'Quantity' = ($($StudentList.Count) - $FilteredNonBlank )
                'Quantity' = $ChangesMade
				'Source' = "$StudentSourceGroupCount StudentSourceGroups"
                'Action' = "$($SimString)Set Department Based on $stringd"
                'Target' = ''
                'OtherInfo' = $FilterString
                'AssignmentErrors' = $TotalErrors
            }
        } else {
            $results += [PSCustomObject]@{
                'Type' = 'Student'
				#'Quantity' = ($($StudentDepartmentList.Count) - $FilteredNonBlank )
                'Quantity' = $ChangesMade
				'Source' = "$StudentSourceGroupCount StudentSourceGroups users That Needed Licensing"
                'Action' = "$($SimString)Set Department Based on $stringd"
                'Target' = ''
                'OtherInfo' = $FilterString
                'AssignmentErrors' = $TotalErrors
            }
        }

		if ($TotalErrors -gt 0 -or $AzureADproblem -gt 0) {
			if ($iniFile.Department.OnFailureApplytoAllUsersOnNextRun -eq $true -or $iniFile.Department.OnFailureApplytoAllUsersOnNextRun -eq 1 -or $iniFile.Department.OnFailureApplytoAllUsersOnNextRun -eq "All" -or $iniFile.Department.OnFailureApplytoAllUsersOnNextRun -eq "Error" -or $iniFile.Department.OnFailureApplytoAllUsersOnNextRun -eq "Errors" -or $iniFile.Department.OnFailureApplytoAllUsersOnNextRun -eq "A" -or $iniFile.Department.OnFailureApplytoAllUsersOnNextRun -eq "Assignment" -or $iniFile.Department.OnFailureApplytoAllUsersOnNextRun -eq "AssignmentError" -or $iniFile.Department.OnFailureApplytoAllUsersOnNextRun -eq "AssignmentErrors") {
				$ApplytoAllonNextRunSummary += [PSCustomObject]@{
					'Function' = 'SetStudentDepartment'
					'Teachers' = 0
					'Students' = $TotalErrors + $AzureADproblem
				}
				$ApplytoAllStudentsOnNextRun=$true
			}
		}

    } else {
        $Actions += "- There are no Students for Department / $stringd Processing"
        write-host "There are no Students for Department / $stringd Processing" -foreground white
        if ($DetailedLoggingEnabled) {
            Add-Content -Path "$LogFile" -Value "There are no Students for Department / $stringd Processing" -Encoding Unicode
        }
    }
}

# Information Retrieval - Get Unique Departments
if ($MajorErrors.Count -eq 0 -and ($iniFile.InformationRetrieval.GetUniqueUserDepartmentNames -eq $true -or $iniFile.InformationRetrieval.GetUniqueUserDepartmentNames -eq 1) ) {

    write-host "Information Retrieval - Get User Unique Department Information" -foregroundcolor green
    
    if ($DetailedLoggingEnabled) {
        Add-Content -Path "$LogFile" -Value "`r`nGet User Unique Department Information" -Encoding Unicode
        Add-Content -Path "$LogFile" -Value "======================================`r`n" -Encoding Unicode
        Add-Content -Path "$LogFile" -Value "Please note that ALL users (including Teachers and Students) are included in this search`r`n" -Encoding Unicode
    }


    $obj = @()

    if ( $iniFile.TeacherSourceGroups.DomainFilter -and $iniFile.StudentSourceGroups.DomainFilter ) {
		$Obj = (AzureADPreview\Get-AzureADUser -all $true | where { $iniFile.StudentSourceGroups.DomainFilter  -contains $_.Mail.Split('@')[1] -or $iniFile.TeacherSourceGroups.DomainFilter -contains $_.Mail.Split('@')[1] } ).department # 1.0.84
        [string]$FilterString = "(filtered against Student Domain Filter $($iniFile.StudentSourceGroups.DomainFilter)) and Teacher Domain Filter $($iniFile.TeacherSourceGroups.DomainFilter))"
    } else {
        if ( $iniFile.StudentSourceGroups.DomainFilter ) {
			$Obj = (AzureADPreview\Get-AzureADUser -all $true | where { $iniFile.StudentSourceGroups.DomainFilter -contains $_.Mail.Split('@')[1] } ).department # 1.0.84
            [string]$FilterString = "(filtered against Student Domain Filter $($iniFile.StudentSourceGroups.DomainFilter))"
        } else {
            if ( $iniFile.TeacherSourceGroups.DomainFilter ) {
				$Obj = (AzureADPreview\Get-AzureADUser -all $true | where { $iniFile.TeacherSourceGroups.DomainFilter -contains $_.Mail.Split('@')[1] } ).department # 1.0.84
                [string]$FilterString = "(filtered against Teacher Domain Filter $($iniFile.TeacherSourceGroups.DomainFilter))"
            } else {
                $Obj = (AzureADPreview\Get-AzureADUser -all $true ).department
                [string]$FilterString = "(not filtered)"
            }
        }
    }
    if ($Obj) {
        $Departments = $Obj | Sort-Object -Unique
    
        #(AzureADPreview\Get-AzureADUser -all $true).department| Sort-Object -Unique
        $Information += "- Get User Unique Department Information: Found $($Obj.Count) AzureAD user records with $($Departments.Count) Unique Department Fields $FilterString"
        $Actions += "- Get User Unique Department Information: Found $($Obj.Count) AzureAD user records with $($Departments.Count) Unique Department Fields $FilterString"
        write-host "Get User Unique Department Information: Found $($Obj.Count) AzureAD user records with $($Departments.Count) Unique Department Fields $FilterString" -ForegroundColor green
        if ($DetailedLoggingEnabled) {
            Add-Content -Path "$LogFile" -Value "AzureAD" -Encoding Unicode
			Add-Content -Path "$LogFile" -Value "-------" -Encoding Unicode
			if ($Departments.count -gt 0) {
				$Departments | fl | Out-String -Width 4096 | Out-File -filepath "$LogFile" -append -Encoding Unicode
			} else {
				Add-Content -Path "$LogFile" -Value "`r`n(no departments)`r`n" -Encoding Unicode	
			}
            Add-Content -Path "$LogFile" -Value "Get User Unique Department Information: Found $($Obj.Count) AzureAD user records with $($Departments.Count) Unique Department Fields $FilterString" -Encoding Unicode
            Add-Content -Path "$LogFile" -Value "Use this information for your Filter strings for Filtered Security Groups / Filtered Unified Groups / Filtered Distribution Groups / Teams Packages / Teams Policies`r`n" -Encoding Unicode            
        }
    } else {
        if ($iniFile.TeacherSourceGroups.DomainFilter -or $iniFile.StudentSourceGroups.DomainFilter) {
            $Warnings += "- Get User Unique Department Information: Could not get the list of all users from AzureAD (check that the TeacherSourceGroups or StudentSourceGroups Domain Filters are correct)"
            write-host "Get User Unique Department Information: Could not get the list of all users from AzureAD (check that the TeacherSourceGroups or StudentSourceGroups Domain Filters are correct)" -foreground red
            if ($DetailedLoggingEnabled) {
                Add-Content -Path "$LogFile" -Value "Get User Unique Department Information: Could not get the list of all users from AzureAD (check that the TeacherSourceGroups or StudentSourceGroups Domain Filters are correct)" -Encoding Unicode
            }
        } else {
            $Warnings += "- Get User Unique Department Information: Could not get the list of all users from AzureAD"
            write-host "Get User Unique Department Information: Could not get the list of all users from AzureAD" -foreground red
            if ($DetailedLoggingEnabled) {
                Add-Content -Path "$LogFile" -Value "Get User Unique Department Information: Could not get the list of all users from AzureAD" -Encoding Unicode
            }
        }
    }
}

#exit 


# $UserOutput State:
# -3 - user was not modified due to default processing - a state record of 2 or 3 already exists for that user
# -2 - user is not licensed or user is disabled
# -1 - user not found - in theory this should never be reported
# 0 - user does not meet the requirements for processing e.g. filter or group membership
# 1 - user does not meet the requirements for processing e.g. filter or group membership, user was removed from the target group
# 2 - user met the requirements and was added to the target group, or the policy or package was applied
# 3 - user met the requirements and did not need modification

if ($MajorErrors.Count -eq 0) {
	if ($FilteredSecurityGroupsToProcess.Count -gt 0 -or $FilteredUnifiedGroupsToProcess.Count -gt 0 -or $FilteredDistributionGroupsToProcess.Count -gt 0 -or $PackagesToProcess.Count -gt 0 -or $PoliciestoProcess.Count -gt 0) {	
	$Output = $null
	$Output = @()
	$Output += [PSCustomObject]@{
			'State' = -3
			'Meaning' = "User was not modified due to default processing - a state record of 2 or 3 already exists for that user"
		}
		$Output += [PSCustomObject]@{
			'State' = -2
			'Meaning' = "User is not licensed or user is disabled"
		}
		$Output += [PSCustomObject]@{
			'State' = -1
			'Meaning' = "(User not found)"
		}	
		$Output += [PSCustomObject]@{
			'State' = 0
			'Meaning' = "User does not meet the requirements for processing e.g. Attribute filter, AD OU/CN rules, Group membership or other requirements"
		}	
		$Output += [PSCustomObject]@{
			'State' = 1
			'Meaning' = "User does not meet the requirements for processing e.g. Attribute filter, AD OU/CN rules, Group membership or other requirements - the user was removed from the target group"
		}
		$Output += [PSCustomObject]@{
			'State' = 2
			'Meaning' = "User met the requirements and was added to the target group, or the package or policy was applied"
		}
		$Output += [PSCustomObject]@{
			'State' = 3
			'Meaning' = "User met the requirements and did not need modification"
		}
	Add-Content -Path "$LogFile" -Value "`r`nUser State Key Table" -Encoding Unicode
    Add-Content -Path "$LogFile" -Value "====================" -Encoding Unicode
	$Output | ft * -AutoSize | Out-String -Width 4096 | Out-File -filepath "$LogFile" -append -Encoding Unicode
	$output = $null
	}
	
	if ($FilteredSecurityGroupsToProcess.Count -gt 0 -or $FilteredUnifiedGroupsToProcess.Count -gt 0 -or $FilteredDistributionGroupsToProcess.Count -gt 0) {	
		$Output = $null
		$Output = @()
		$Output += [PSCustomObject]@{
			'Letter' = "A"
			'Meaning' = "Remove unmatched members from the Group that don't match any Attribute filter"
		}
		$Output += [PSCustomObject]@{
			'Letter' = "G"
			'Meaning' = "Remove unmatched members from the Group that don't match any Group rules"
		}
		$Output += [PSCustomObject]@{
			'Letter' = "O"
			'Meaning' = "Remove unmatched members from the Group that don't match any AD OU/CN rules"
		}
		$Output += [PSCustomObject]@{
			'Letter' = "C"
			'Meaning' = "Remove unmatched members from the Group by CollectionID Processing"
		}		
	Add-Content -Path "$LogFile" -Value "`r`nRemove Unmatched Members from Group Key Table" -Encoding Unicode
    Add-Content -Path "$LogFile" -Value "=============================================" -Encoding Unicode
	$Output | ft * -AutoSize | Out-String -Width 4096 | Out-File -filepath "$LogFile" -append -Encoding Unicode
	$output = $null
	}
}


# process filtered Security Groups
if ($MajorErrors.Count -eq 0) {
    [int]$ApplytoAllTeachersOnNextRunThisFunc=0
	[int]$ApplytoAllStudentsOnNextRunThisFunc=0
	if ($FilteredSecurityGroupsToProcess.Count -gt 0) {
        write-host "Applying Filtered Security Groups" -foregroundcolor green        
        if ($DetailedLoggingEnabled) {
            Add-Content -Path "$LogFile" -Value "`r`nApplying Filtered Security Groups" -Encoding Unicode
            Add-Content -Path "$LogFile" -Value "=================================`r`n" -Encoding Unicode
            Add-Content -Path "$LogFile" -Value "Group Changes to Process:" -Encoding Unicode
            Add-Content -Path "$LogFile" -Value "-------------------------" -Encoding Unicode
            $FilteredSecurityGroupsToProcess | select -Property * -ExcludeProperty CollectionIDsToExclude | ft * -AutoSize | Out-String -Width 4096 | Out-File -filepath "$LogFile" -append -Encoding Unicode # 1.0.94
			Add-Content -Path "$LogFile" -Value "Note: If the global filter type setting is set to Remote, the filter source is forced to CSOnline`r`n" -Encoding Unicode
        }
        [int]$GroupsProcessed=0
        $Filter=$null
		$FilterSource=$null
        $OnlineUsers= @()
		$UnfilteredOnlineUsers = @()
        $UserOutput = @()
		$PendingRemovals = @()
		$FilteredPendingRemovals = @()
        $Output = @()
        
        [int]$GroupGroupNameMaxLength=($FilteredSecurityGroupsToProcess | Select GroupName | Sort -Property { $_.GroupName.length} | select -Last 1).GroupName.Length
		[int]$SettingsMaxLength=($FilteredSecurityGroupsToProcess | Select Settings | Sort -Property { $_.Settings.length} | select -Last 1).Settings.Length
		[int]$CollectionIDMaxLength=(($FilteredSecurityGroupsToProcess | Select CollectionID | Sort -Property { $_.CollectionID} | select -Last 1).CollectionID -as [string]).length
        if ( @($FilteredSecurityGroupsToProcess | where { $_.RemoveUnMatchedMembers -gt 0 }).count -gt 0) {
            $HasRemoveUnMatchedMembers = $true
        } else {
            $HasRemoveUnMatchedMembers = $false
        }

        if ( @($FilteredSecurityGroupsToProcess | where { $_.RemoveUnMatchedMembers -gt 0 -and $_.OnlyRemoveSameType -eq $false }).count -gt 0) {
            $OnlyRemoveSameTypemaxLength = 5
        } else {
            $OnlyRemoveSameTypemaxLength = 4
        }

        $AzureUsers = $null
		
		[int]$UsersRemoved=0
        foreach ($Group in $FilteredSecurityGroupsToProcess) {
            [int]$UsersAdded=0
 
			[int]$PendingRemovalThisGroup=0
			
            [string]$string = ''
			[string]$stringb = ''

            $GroupsProcessed = $GroupsProcessed +1

            if ($Group.Description) {
                $DescString = "$($Group.Description)"
            } else {
                $DescString = "-"
            }

            $ProcessedString = [string]$GroupsProcessed
			$ProcessedString = $ProcessedString.PadLeft(($FilteredSecurityGroupsToProcess.Count -as [string]).length," ")
			
            $PadGroupName=$Group.GroupName.PadRight($GroupGroupNameMaxLength," ")

            $PadSettings=$Group.Settings.PadRight($SettingsMaxLength," ")
			$PadCollectionID=([string]$Group.CollectionID).PadRight($CollectionIDMaxLength," ")
            #$PadUnMatchedMembers = [string]$($Group.RemoveUnMatchedMembers)
            #$PadUnMatchedMembers = $PadUnMatchedMembers.PadRight($RemoveUnMatchedMembersmaxLength," ")

            if ($Group.RemoveUnMatchedMembers -gt 0) {
				$UnMatchedMembers = ""
				if (($Group.RemoveUnMatchedMembers -band 2) -eq 2)  { $UnMatchedMembers = "A" }
				if (($Group.RemoveUnMatchedMembers -band 4) -eq 4)  { $UnMatchedMembers = "$($UnMatchedMembers)G" }
				if (($Group.RemoveUnMatchedMembers -band 8) -eq 8)  { $UnMatchedMembers = "$($UnMatchedMembers)O" }
				if (($Group.RemoveUnMatchedMembers -band 16) -eq 16)  { $UnMatchedMembers = "$($UnMatchedMembers)C" }
				$PadUnMatchedMembers = $UnMatchedMembers.PadRight(4," ")
                $PadOnlyRemoveSameType = [string]$($Group.OnlyRemoveSameType)
                $PadOnlyRemoveSameType = $PadOnlyRemoveSameType.PadRight($OnlyRemoveSameTypemaxLength," ")
                $PadOnlyRemoveSameType = "- $($Group.UserType)s Only: $PadOnlyRemoveSameType "
            } else {
                $PadUnMatchedMembers = "    "
				$UnMatchedMembers = $false
				$PadOnlyRemoveSameType=""
                if ($HasRemoveUnMatchedMembers -eq $true) {
                    $PadOnlyRemoveSameType = $PadOnlyRemoveSameType.PadRight($OnlyRemoveSameTypemaxLength," ")
                    $PadOnlyRemoveSameType = "                $PadOnlyRemoveSameType "
                }
            }

            write-host "$($SimString)Processing Group $ProcessedString of $($FilteredSecurityGroupsToProcess.Count): CollectionID: $PadCollectionID   $PadSettings   Group ID/Name: $PadGroupName   Remove UnMatched Members: $PadUnMatchedMembers $PadOnlyRemoveSameType      Desc: $DescString" -ForegroundColor $textcolour

            if ($DetailedLoggingEnabled) {
                Add-Content -Path "$LogFile" -Value "$($SimString)Processing Group $ProcessedString of $($FilteredSecurityGroupsToProcess.Count): CollectionID: $PadCollectionID   $PadSettings   Group ID/Name: $PadGroupName   Remove UnMatched Members: $PadUnMatchedMembers $PadOnlyRemoveSameType      Desc: $DescString" -Encoding Unicode
            }
			
			$string=$null
			$string=""			
            if ($Group.UserType -eq 'Student') {
                if ($Group.ApplyToAll -eq $true -or $ApplytoAllStudents  -eq $true) {
                    $UsersToProcess = $StudentList
                } else {
                    $UsersToProcess = $StudentsNotInLicensingGroup
					$string=" that were not members of the Licensing Group"
                }
            }
            if ($Group.UserType -eq 'Teacher') {
                if ($Group.ApplyToAll -eq $true -or $ApplytoAllTeachers -eq $true) {
                    $UsersToProcess = $TeacherList
                } else {
                    $UsersToProcess = $TeachersNotInLicensingGroup
					$string=" that were not members of the Licensing Group"
                }
            }

			if ($Group.DomainFilter ) {
				$UsersToProcess = @($UsersToProcess | where { $Group.DomainFilter -contains $_.EmailAddress.Split('@')[1] })
				$string="$string and were filtered by the Domain Filter"
			}
			
            if ($Group.OnlyApplyToUnlicensed -eq $true) {
                $UsersToProcess = @($UsersToProcess | where { $_.IsLicensed -eq $false })
				write-host "$($SimString)Using $($UsersToProcess.count) Unlicensed Source List $($Group.UserType)s$String" -ForegroundColor $textcolour	
				if ($DetailedLoggingEnabled) {
					Add-Content -Path "$LogFile" -Value "$($SimString)Using $($UsersToProcess.count) Unlicensed Source List $($Group.UserType)s$String"
				}				
            } else {
				write-host "$($SimString)Using $($UsersToProcess.count) Source List $($Group.UserType)s$String" -ForegroundColor $textcolour	
				if ($DetailedLoggingEnabled) {
					Add-Content -Path "$LogFile" -Value "$($SimString)Using $($UsersToProcess.count) Source List $($Group.UserType)s$String"
				}	
			}
			$string=$null
			
            $RequiredRecursiveIDs = @()
            $NotRequiredRecursiveIDs = @()
            $ProblemGroups = @()
            $GroupRulesOK = $true
			$GotGroupUsersOK = $true
			$GroupMatchedUsers = @()
            if ($Group.HasGroupRules -eq $true ) {

                write-host "$($SimString)Building Group Rules Table" -ForegroundColor $textcolour
                
                if ($DetailedLoggingEnabled) {
                    Add-Content -Path "$LogFile" -Value "`r`n$($SimString)Building Group Rules Table`r`n" -Encoding Unicode
                }
				$e = $error.count
                $GroupRulesOK, $ProblemGroups, $RequiredRecursiveIDs, $RequiredORRecursiveIDs, $NotRequiredRecursiveIDs = BuildGroupTable -GroupInformation $Group

                if ($GroupRulesOK -eq $true) {
                    write-host "$($SimString)Group Rules OK: $GroupRulesOK   Problem Groups: $($ProblemGroups.count)   RequiredGroups: $($RequiredRecursiveIDs.count)   RequiredOrGroups: $($RequiredORRecursiveIDs.count)   NotRequiredGroups: $($NotRequiredRecursiveIDs.count)" -ForegroundColor $textcolour
					$GroupMatchedUsers = GetGroupRuleUsers -RequiredRecursiveIDs $RequiredRecursiveIDs -RequiredORRecursiveIDs $RequiredORRecursiveIDs -NotRequiredRecursiveIDs $NotRequiredRecursiveIDs
					write-host "$($SimString)Found $($GroupMatchedUsers.count) users matching the Group Rules" -ForegroundColor $textcolour
                } else {
                    write-host "$($SimString)Group Rules OK: $GroupRulesOK   Problem Groups: $($ProblemGroups.count)   RequiredGroups: $($RequiredRecursiveIDs.count)   RequiredOrGroups: $($RequiredORRecursiveIDs.count)   NotRequiredGroups: $($NotRequiredRecursiveIDs.count)" -ForegroundColor yellow
                    $warnings += "- $($SimString)Filtered Security Group $($GroupsProcessed): Group Rules OK: $GroupRulesOK   Problem Groups: $($ProblemGroups.count)   RequiredGroups: $($RequiredRecursiveIDs.count)   RequiredOrGroups: $($RequiredORRecursiveIDs.count)   NotRequiredGroups: $($NotRequiredRecursiveIDs.count)"

                }
                if ($error.count -gt $e) {
                    $errorstring = ""
			        if ($Error[0].Exception.Message) {
				        $errorstring = $Error[0].Exception.Message
			        } else {
				        $errorstring = "(unknown)"
			        }
					write-host "There was an error in processing Group Rules or matching users - error message: $errorstring" -foreground red
					if ($DetailedLoggingEnabled) {
						Add-Content -Path "$LogFile" -Value "There was an error in processing Group Rules or matching users - error message: $errorstring" -Encoding Unicode
					}
					$Warnings += "- $($SimString)Filtered Security Group $($GroupsProcessed): There was an error in processing Group Rules or matching users - error message: $errorstring"
					$GotGroupUsersOK = $false
                }

                if ($DetailedLoggingEnabled) {
                    if ($RequiredRecursiveIDs.count -gt 0) {
						Add-Content -Path "$LogFile" -Value "Group Rules: RequiredGroups, Including Recursive"
                        Add-Content -Path "$LogFile" -Value "------------------------------------------------"
                        $RequiredRecursiveIDs | Sort DisplayName,  CommonName, Description, ObjectID | fl * | Out-String -Width 4096 | Out-File -filepath "$LogFile" -append -Encoding Unicode
                    }
                    if ($RequiredORRecursiveIDs.count -gt 0) {
						Add-Content -Path "$LogFile" -Value "Group Rules: RequiredOrGroups, Including Recursive"
                        Add-Content -Path "$LogFile" -Value "--------------------------------------------------"
                        $RequiredORRecursiveIDs | Sort DisplayName,  CommonName, Description, ObjectID | fl * | Out-String -Width 4096 | Out-File -filepath "$LogFile" -append -Encoding Unicode
                    }					
                    if ($NotRequiredRecursiveIDs.count -gt 0) {
						Add-Content -Path "$LogFile" -Value "Group Rules: NotRequiredGroups, Including Recursive"
                        Add-Content -Path "$LogFile" -Value "---------------------------------------------------"
                        $NotRequiredRecursiveIDs | Sort DisplayName,  CommonName, Description, ObjectID | fl * | Out-String -Width 4096 | Out-File -filepath "$LogFile" -append -Encoding Unicode
                    }
                    if ($ProblemGroups.count -gt 0) {
                        Add-Content -Path "$LogFile" -Value "Group Rules: Problem Groups"
                        Add-Content -Path "$LogFile" -Value "---------------------------"
                        $ProblemGroups | Sort DisplayName,  CommonName, Description, ObjectID | fl * | Out-String -Width 4096 | Out-File -filepath "$LogFile" -append -Encoding Unicode
                    }
                    Add-Content -Path "$LogFile" -Value "$($SimString)Group Rules OK: $GroupRulesOK   Problem Groups: $($ProblemGroups.count)   RequiredGroups: $($RequiredRecursiveIDs.count)   RequiredOrGroups: $($RequiredORRecursiveIDs.count)   NotRequiredGroups: $($NotRequiredRecursiveIDs.count)" -Encoding Unicode
					Add-Content -Path "$LogFile" -Value "$($SimString)Found $($GroupMatchedUsers.count) users matching the Group Rules" -Encoding Unicode
                }
            }
# end 1.0.21

			if ($Group.HasADOURules -eq $true ) {
					write-host "$($SimString)Active Directory OU/CN Rules - Included OU/CN Or Rules: $($Group.RequiredOrOUs.count)   Not Required OU/CN Rules: $($Group.NotRequiredOUs.count)" -ForegroundColor $textcolour	
				
				if ($DetailedLoggingEnabled) {
					Add-Content -Path "$LogFile" -Value "`r`nActive Directory OU/CN Rules"
                    Add-Content -Path "$LogFile" -Value "----------------------------"
					$Group | select RequiredOrOUs, NotRequiredOUs | Sort RequiredOrOUs, NotRequiredOUs | ft * -AutoSize | Out-String -Width 4096 | Out-File -filepath "$LogFile" -append -Encoding Unicode
	
					Add-Content -Path "$LogFile" -Value "$($SimString)Included OU/CN Or Rules: $($Group.RequiredOrOUs.count)   Not Required OU/CN Rules: $($Group.NotRequiredOUs.count)"
				}
			}
			
            $TargetSecurityGroup = $null
            if ($Group.GroupNameAsID -eq $true) {
                $TargetSecurityGroup = @(Get-MsolGroup -ObjectID $Group.GroupName)
            } else {
                $TargetSecurityGroup = @(Get-MsolGroup -SearchString $Group.GroupName | where {$_.GroupType -eq 'Security' -or $_.GroupType -eq 'MailEnabledSecurity'} )
                if ($Group.DisableNameOrIDChecking -eq $false) {
                    $TargetSecurityGroup = $TargetSecurityGroup | where { $_.CommonName -like $Group.GroupName -or $_.DisplayName -like $Group.GroupName -or $_.EmailAddress -like $Group.GroupName } # 1.0.21
                }
            }
            
            
            #if ( @($TargetSecurityGroup).count -eq 1) {
            if ($TargetSecurityGroup.count -eq 1) { # 1.0.6
                if ($GotGroupUsersOK -eq $true) {
					if ($GroupRulesOK -eq $true) {
						$e = $error.count

						if ($Group.HasADOURules -eq $true -and !$AzureUsers) {
							$AzureUsers = @(AzureADPreview\Get-AzureADUser -all $true -ErrorAction SilentlyContinue -WarningAction SilentlyContinue)
						}

						# Get All Filtered Online Users
						if ($Group.ActionFilter) {
							$FilteringEnabled = $true
							if ($Filter -ne $Group.ActionFilter -or $FilterSource -ne $Group.FilterSource) {
								$Filter = $Group.ActionFilter
								$FilterSource = $Group.FilterSource
								
								if ($UseLocalAttributeFiltering -eq $false) {
									$OnlineUsers = @(Get-CSOnlineUser -Filter $Filter -ErrorAction SilentlyContinue -WarningAction SilentlyContinue)
									$ObjectField = 'Identity'
								} else {
									if ($FilterSource -eq 'AzureAD') {
										if (!$UnfilteredOnlineUsers) {
											if ($AzureUsers) {
												$UnfilteredOnlineUsers = @($AzureUsers)
											} else {
												$UnfilteredOnlineUsers = @(AzureADPreview\Get-AzureADUser -all $true -ErrorAction SilentlyContinue -WarningAction SilentlyContinue)
											}
										}
										$OnlineUsers = @($UnfilteredOnlineUsers | where { Invoke-Expression $Filter })
										$ObjectField = 'ObjectID'
									} else {
										if (!$UnfilteredOnlineUsers) {
											$UnfilteredOnlineUsers = @(Get-CSOnlineUser -ErrorAction SilentlyContinue -WarningAction SilentlyContinue)
										}
										$OnlineUsers = @($UnfilteredOnlineUsers | where { Invoke-Expression $Filter })
										$ObjectField = 'Identity'
									}
								}
							}
						} else {
							$FilteringEnabled = $false
							#if ($Filter -or $OnlineUsers.count -eq 0) {
								#$Filter = $null
								#$FilterSource = $Group.FilterSource
								#if ($UseLocalAttributeFiltering -eq $false) {
									#$OnlineUsers = @(Get-CSOnlineUser -ErrorAction SilentlyContinue -WarningAction SilentlyContinue)
								#} else {
									#if ($FilterSource -eq 'AzureAD') {
										#if (!$UnfilteredOnlineUsers) {
											#$UnfilteredOnlineUsers = @(AzureADPreview\Get-AzureADUser -all $true -ErrorAction SilentlyContinue -WarningAction SilentlyContinue)
										#}
										#$OnlineUsers = @($UnfilteredOnlineUsers)
									#} else {
										#if (!$UnfilteredOnlineUsers) {
											#$UnfilteredOnlineUsers = @(Get-CSOnlineUser -ErrorAction SilentlyContinue -WarningAction SilentlyContinue)
										#}
										#$OnlineUsers = @($UnfilteredOnlineUsers)
									#}
								#}
							#}
						}

						if ($error.count -le $e) {
						# Get Existing Group Members

							$ExistingMembers = $null
							$ExistingMembers = @(get-msolgroupmember -GroupObjectId $TargetSecurityGroup.objectid -All)
							$ExistingMembers = @( $ExistingMembers | Sort EmailAddress ) # 1.0.61

							if ($Group.RemoveUnMatchedMembers -gt 0) {                    
								if ($Group.UserType -eq 'Teacher') {
									$BaseUserGroup = $Teacherlist
								} else {
									$BaseUserGroup = $Studentlist
								}

								[int]$ResultCount=0
								[int]$personprocessed=0
								$funcstarttime = (Get-Date)
								
								write-host "$($simstring)Processing Filtered Security Group Existing Members - CollectionID: $PadCollectionID   $PadSettings   Group ID/Name: $PadGroupName   Users to Process: $($ExistingMembers.Count)" -ForegroundColor $textcolour

								if ($DetailedLoggingEnabled) {
									Add-Content -Path "$LogFile" -Value "$($simstring)Processing Filtered Security Group Existing Members - CollectionID: $PadCollectionID   $PadSettings   Group ID/Name: $PadGroupName   Users to Process: $($ExistingMembers.Count)" -Encoding Unicode  
								}							

								foreach ($ExistingMember in $ExistingMembers) { # compare existing members against csonlineusers (filtered), but make sure that user type is matched
									$ResultCount = $ResultCount + 1
									$personprocessed = $personprocessed + 1
									#if ($Group.OnlyRemoveSameType -eq $false -or $BaseUserGroup.EmailAddress -contains $ExistingMember.EmailAddress) {
									if ($Group.OnlyRemoveSameType -eq $false -or $BaseUserGroup.ObjectID -contains $ExistingMember.ObjectID) { # 1.0.85
														  
										$OU = $null
										$OU = ($AzureUsers | where {$_.ObjectID -eq $ExistingMember.ObjectID} | Select -First 1).ExtensionProperty.onPremisesDistinguishedName
																				
										if ( ($FilteringEnabled -eq $true -and (($Group.RemoveUnMatchedMembers -band 2) -eq 2) -and $OnlineUsers.($ObjectField) -notcontains $ExistingMember.ObjectID) -or ($Group.HasGroupRules -eq $true -and (($Group.RemoveUnMatchedMembers -band 4) -eq 4) -and ($GroupMatchedUsers.ObjectID -notcontains $ExistingMember.ObjectID)) -or ($Group.HasADOURules -eq $true -and (($Group.RemoveUnMatchedMembers -band 8) -eq 8) -and $OU -and (Check_ADCanonicalName -UserCN $OU -RequiredOrOUs $Group.RequiredOrOUs -NotRequiredOUs $Group.NotRequiredOUs) -ne $true) -or ( (($Group.RemoveUnMatchedMembers -band 16) -eq 16) -and @($UserOutput | where {$_.CollectionID -gt $Group.CollectionIDIgnoreThreshold -and $_.CollectionID -lt $Group.CollectionID -and $_.CollectionID -notin @($Group.CollectionIDsToExclude) -and $_.UserID -eq $ExistingMember.ObjectID -and ($_.UserType -eq $Group.UserType -or $IgnoreCollectionIDUserType -eq $true) -and $_.State -gt 1 }).count -gt 0)) { # 1.0.91
											#  write-host "$($ExistingMember.EmailAddress) would be removed"


											if ( @($UserOutput | where {$_.GroupID -eq $TargetSecurityGroup.objectid -and $_.UserID -eq $ExistingMember.ObjectID -and $_.State -gt 1 }).count -eq 0) { # user shouldnt be removed if already added by another settings


												if ( @($PendingRemovals | where { $_.GroupID -eq $TargetSecurityGroup.objectid -and $_.UserID -eq $ExistingMember.ObjectID}).count -eq 0) {	# dont put the user in pending removal for this group if already there
													$PendingRemovalThisGroup +=1
													
													if ($PendingRemovalThisGroup -eq 1) {
														$string = $ExistingMember.EmailAddress
														$string = ", including: $string"
													}
													
													$PendingRemovals += [PSCustomObject]@{
														'UserType' = $Group.UserType
														'Settings' = $Group.Settings
														#'Default' = $Group.Default
														'CollectionID' = $Group.CollectionID
														#'CollectionIDIgnoreThreshold' = $Group.CollectionIDIgnoreThreshold
														'DisplayName' = $ExistingMember.DisplayName
														'EmailAddress' = $ExistingMember.EmailAddress
														'Action' = "Removed From Group"
														'GroupName' = $TargetSecurityGroup.DisplayName
														'UserID' = $ExistingMember.ObjectID
														'GroupID' = $TargetSecurityGroup.objectid
														'GroupDescription' = $TargetSecurityGroup.Description
														'GroupCN' = $TargetSecurityGroup.CommonName
														'Simulation' = !$MakeChanges
														'Filter' = $Group.ActionFilter
														'NumberofFilteredUsers' = $OnlineUsers.Count
														'RemoveUnMatchedMembers' = $UnMatchedMembers
														'OnlyRemoveSameType' = $Group.OnlyRemoveSameType
														'State' = 1
														'SettingsNumber' = $Group.SettingsNumber
														'SettingsDescription' = $Group.Description
														'ErrorGenerated' = '-'
														'ErrorMessage' = '-'
													}
												}

											} else {
												$UserOutput += [PSCustomObject]@{
													'UserType' = $Group.UserType
													'Settings' = $Group.Settings
													#'Default' = $Group.Default
													'CollectionID' = $Group.CollectionID
													#'CollectionIDIgnoreThreshold' = $Group.CollectionIDIgnoreThreshold
													'DisplayName' = $ExistingMember.DisplayName
													'EmailAddress' = $ExistingMember.EmailAddress
													'Action' = "Not Removed From Group As Already Added or Matched"
													'GroupName' = $TargetSecurityGroup.DisplayName
													'UserID' = $ExistingMember.ObjectID
													'GroupID' = $TargetSecurityGroup.objectid
													'GroupDescription' = $TargetSecurityGroup.Description
													'GroupCN' = $TargetSecurityGroup.CommonName
													'Simulation' = !$MakeChanges
													'Filter' = $Group.ActionFilter
													'NumberofFilteredUsers' = $OnlineUsers.Count
													'RemoveUnMatchedMembers' = $UnMatchedMembers
													'OnlyRemoveSameType' = $Group.OnlyRemoveSameType
													'SettingsNumber' = $Group.SettingsNumber
													'SettingsDescription' = $Group.Description
													'State' = -3
													'ErrorGenerated' = '-'
													'ErrorMessage' = '-'
												}	
											}
										}
									}
									
									if ($ResultCount -eq $LongJobUpdateInterval) {
										[int]$ResultCount=0
										
										$UsersProcessedString, $timestring, $timeremainingseconds = get-timeelapsed -StartTime $funcstarttime -Processed $personprocessed -ToProcess ($ExistingMembers.Count)
										
										if ($timeremainingseconds -ge 10) {

											write-host "$($simstring)Processing Filtered Security Group Existing Members - CollectionID: $PadCollectionID   $PadSettings   Group ID/Name: $PadGroupName   Processed $UsersProcessedString of $($ExistingMembers.Count) users - ETR$timestring" -ForegroundColor Gray

											if ($DetailedLoggingEnabled) {
												Add-Content -Path "$LogFile" -Value "$($simstring)Processing Filtered Security Group Existing Members - CollectionID: $PadCollectionID   $PadSettings   Group ID/Name: $PadGroupName   Processed $UsersProcessedString of $($ExistingMembers.Count) users - ETR$timestring" -Encoding Unicode  
											}
										}
									}
								}
							} else {
								write-host "$($simstring)Processing Filtered Security Group Existing Members - CollectionID: $PadCollectionID   $PadSettings   Group ID/Name: $PadGroupName   Users to Process: 0 (unmatched users not being removed)" -ForegroundColor $textcolour

								if ($DetailedLoggingEnabled) {
									Add-Content -Path "$LogFile" -Value "$($simstring)Processing Filtered Security Group Existing Members - CollectionID: $PadCollectionID   $PadSettings   Group ID/Name: $PadGroupName   Users to Process: 0 (unmatched users not being removed)" -Encoding Unicode  
								}							
							}

							# Add people from $UsersToProcess.EmailAddress to the group $TargetSecurityGroup where they are in the filtered list $OnlineUsers.UserPrincipalName AND NOT already in the $ExistingMember.EmailAddress
							[int]$ResultCount=0
							[int]$personprocessed=0
							$funcstarttime = (Get-Date)
							
							foreach ($User in $UsersToProcess) {
								$ResultCount = $ResultCount + 1
								$personprocessed = $personprocessed + 1
								$GroupContainsUser = $false
								#if ($ExistingMembers.EmailAddress -contains $User.EmailAddress) { $GroupContainsUser = $true }
								if ($ExistingMembers.ObjectID -contains $User.ObjectID) { $GroupContainsUser = $true } # 1.0.84

								if ( ($FilteringEnabled -eq $false -or $OnlineUsers.($ObjectField) -contains $User.ObjectID) -and $GroupContainsUser -eq $false -and ($Group.HasGroupRules -eq $false -or ($GroupMatchedUsers.ObjectID -contains $User.ObjectID))) { # 1.0.84


									if ($Group.HasADOURules -eq $true) {
										$OU = $null
										$OU = ($AzureUsers | where {$_.ObjectID -eq $User.ObjectID} | Select -First 1).ExtensionProperty.onPremisesDistinguishedName
									}

									if ($Group.HasADOURules -eq $false -or ( (Check_ADCanonicalName -UserCN $OU -RequiredOrOUs $Group.RequiredOrOUs -NotRequiredOUs $Group.NotRequiredOUs) -eq $true)) {

										if ($Group.CollectionID -eq 0 -or ( @($UserOutput | where {$_.CollectionID -gt $Group.CollectionIDIgnoreThreshold -and $_.CollectionID -lt $Group.CollectionID -and $_.CollectionID -notin @($Group.CollectionIDsToExclude) -and $_.UserID -eq $User.ObjectID -and ($_.UserType -eq $Group.UserType -or $IgnoreCollectionIDUserType -eq $true) -and $_.State -gt 1 }).count -eq 0)) { #1.0.86
											$UsersAdded +=1

											if ($UsersAdded -eq 1) {
												$stringb = $user.EmailAddress
												$stringb = ", including: $stringb"
											}

											$ErrorGenerated=$false
											$errorstring = ""
											if ($MakeChanges -eq $true) {
												$e = $error.count
												Add-MsolGroupMember -GroupObjectId $TargetSecurityGroup.objectid -GroupMemberType User -GroupMemberObjectId $User.ObjectID -ErrorAction SilentlyContinue
												if ($error.count -gt $e) {
													if ($Error[0].Exception.Message) {
														$errorstring = $Error[0].Exception.Message
													} else {
														$errorstring = "(unknown)"
													}
													$ErrorGenerated=$true
														
													if ( ($Group.OnFailureApplytoAllUsersOnNextRun -band 2) -eq 2 -or ($GlobalOnFailureApplytoAllUsersOnNextRun -band 2) -eq 2) {
														if ($Group.UserType -eq 'Teacher') {
															$ApplytoAllTeachersOnNextRunThisFunc+=1
														} else {
															$ApplytoAllStudentsOnNextRunThisFunc+=1		
														}
													}
												}
											}
									
											$PendingRemovals = @($PendingRemovals | where { $_.UserID -ne $User.ObjectID -or $_.GroupID -ne $TargetSecurityGroup.objectid} )
											# $PendingRemovals = @($PendingRemovals | where { !($_.UserID -eq $User.ObjectID -and $_.GroupID -eq $TargetSecurityGroup.objectid)} ) # alt
									
											$UserOutput += [PSCustomObject]@{
												'UserType' = $Group.UserType
												'Settings' = $Group.Settings
												#'Default' = $Group.Default
												'CollectionID' = $Group.CollectionID
												#'CollectionIDIgnoreThreshold' = $Group.CollectionIDIgnoreThreshold
												'DisplayName' = $user.DisplayName
												'EmailAddress' = $user.EmailAddress
												'Action' = "Added To Group"
												'GroupName' = $TargetSecurityGroup.DisplayName
												'UserID' = $User.ObjectID
												'GroupID' = $TargetSecurityGroup.objectid
												'GroupDescription' = $TargetSecurityGroup.Description
												'GroupCN' = $TargetSecurityGroup.CommonName
												'Simulation' = !$MakeChanges
												'Filter' = $Group.ActionFilter
												'NumberofFilteredUsers' = $OnlineUsers.Count
												'RemoveUnMatchedMembers' = $UnMatchedMembers
												'OnlyRemoveSameType' = $Group.OnlyRemoveSameType
												'SettingsNumber' = $Group.SettingsNumber
												'SettingsDescription' = $Group.Description
												'State' = 2
												'ErrorGenerated' = $ErrorGenerated
												'ErrorMessage' = $errorstring
											}
										} else {
											$UserOutput += [PSCustomObject]@{
												'UserType' = $Group.UserType
												'Settings' = $Group.Settings
												#'Default' = $Group.Default
												'CollectionID' = $Group.CollectionID
												#'CollectionIDIgnoreThreshold' = $Group.CollectionIDIgnoreThreshold
												'DisplayName' = $user.DisplayName
												'EmailAddress' = $user.EmailAddress
												'Action' = "User filtered by CollectionID"
												'GroupName' = $TargetSecurityGroup.DisplayName
												'UserID' = $User.ObjectID
												'GroupID' = $TargetSecurityGroup.objectid
												'GroupDescription' = $TargetSecurityGroup.Description
												'GroupCN' = $TargetSecurityGroup.CommonName
												'Simulation' = !$MakeChanges
												'Filter' = $Group.ActionFilter
												'NumberofFilteredUsers' = $OnlineUsers.Count
												'RemoveUnMatchedMembers' = $UnMatchedMembers
												'OnlyRemoveSameType' = $Group.OnlyRemoveSameType
												'SettingsNumber' = $Group.SettingsNumber
												'SettingsDescription' = $Group.Description
												'State' = -3
												'ErrorGenerated' = '-'
												'ErrorMessage' = '-'
											}
										}
									} else {

										$UserOutput += [PSCustomObject]@{
											'UserType' = $Group.UserType
											'Settings' = $Group.Settings
											#'Default' = $Group.Default
											'CollectionID' = $Group.CollectionID
											#'CollectionIDIgnoreThreshold' = $Group.CollectionIDIgnoreThreshold
											'DisplayName' = $user.DisplayName
											'EmailAddress' = $user.EmailAddress
											'Action' = "User Filtered by AD OU/CN Rules"
											'GroupName' = $TargetSecurityGroup.DisplayName
											'UserID' = $User.ObjectID
											'GroupID' = $TargetSecurityGroup.objectid
											'GroupDescription' = $TargetSecurityGroup.Description
											'GroupCN' = $TargetSecurityGroup.CommonName
											'Simulation' = !$MakeChanges
											'Filter' = $Group.ActionFilter
											'NumberofFilteredUsers' = $OnlineUsers.Count
											'RemoveUnMatchedMembers' = $UnMatchedMembers
											'OnlyRemoveSameType' = $Group.OnlyRemoveSameType
											'SettingsNumber' = $Group.SettingsNumber
											'SettingsDescription' = $Group.Description
											'State' = 0
											'ErrorGenerated' = '-'
											'ErrorMessage' = '-'
										 }
									}
								} else {
									if ($GroupContainsUser -eq $false) {
										 $UserOutput += [PSCustomObject]@{
											'UserType' = $Group.UserType
											'Settings' = $Group.Settings
											#'Default' = $Group.Default
											'CollectionID' = $Group.CollectionID
											#'CollectionIDIgnoreThreshold' = $Group.CollectionIDIgnoreThreshold
											'DisplayName' = $user.DisplayName
											'EmailAddress' = $user.EmailAddress
											'Action' = "User Filtered by Group Rules or $($Group.FilterSource)"
											'GroupName' = $TargetSecurityGroup.DisplayName
											'UserID' = $User.ObjectID
											'GroupID' = $TargetSecurityGroup.objectid
											'GroupDescription' = $TargetSecurityGroup.Description
											'GroupCN' = $TargetSecurityGroup.CommonName
											'Simulation' = !$MakeChanges
											'Filter' = $Group.ActionFilter
											'NumberofFilteredUsers' = $OnlineUsers.Count
											'RemoveUnMatchedMembers' = $UnMatchedMembers
											'OnlyRemoveSameType' = $Group.OnlyRemoveSameType
											'SettingsNumber' = $Group.SettingsNumber
											'SettingsDescription' = $Group.Description
											'State' = 0
											'ErrorGenerated' = '-'
											'ErrorMessage' = '-'
										 }
									} else {
										$UserOutput += [PSCustomObject]@{
											'UserType' = $Group.UserType
											'Settings' = $Group.Settings
											#'Default' = $Group.Default
											'CollectionID' = $Group.CollectionID
											#'CollectionIDIgnoreThreshold' = $Group.CollectionIDIgnoreThreshold
											'DisplayName' = $user.DisplayName
											'EmailAddress' = $user.EmailAddress
											'Action' = "Group contains User"
											'GroupName' = $TargetSecurityGroup.DisplayName
											'UserID' = $User.ObjectID
											'GroupID' = $TargetSecurityGroup.objectid
											'GroupDescription' = $TargetSecurityGroup.Description
											'GroupCN' = $TargetSecurityGroup.CommonName
											'Simulation' = !$MakeChanges
											'Filter' = $Group.ActionFilter
											'NumberofFilteredUsers' = $OnlineUsers.Count
											'RemoveUnMatchedMembers' = $UnMatchedMembers
											'OnlyRemoveSameType' = $Group.OnlyRemoveSameType
											'SettingsNumber' = $Group.SettingsNumber
											'SettingsDescription' = $Group.Description
											'State' = 3
											'ErrorGenerated' = '-'
											'ErrorMessage' = '-'
										 }									
									}
								}
								if ($ResultCount -eq $LongJobUpdateInterval) {
									[int]$ResultCount=0
									
									$UsersProcessedString, $timestring, $timeremainingseconds = get-timeelapsed -StartTime $funcstarttime -Processed $personprocessed -ToProcess ($UsersToProcess.Count)
									
									if ($timeremainingseconds -ge 10) {

										write-host "$($simstring)Processing Filtered Security Group User Additions - CollectionID: $PadCollectionID   $PadSettings   Group ID/Name: $PadGroupName   Processed $UsersProcessedString of $($UsersToProcess.Count) potential users - ETR$timestring" -ForegroundColor Gray

										if ($DetailedLoggingEnabled) {
											Add-Content -Path "$LogFile" -Value "$($simstring)Processing Filtered Security Group User Additions - CollectionID: $PadCollectionID   $PadSettings   Group ID/Name: $PadGroupName   Processed $UsersProcessedString of $($UsersToProcess.Count) potential users - ETR$timestring" -Encoding Unicode  
										}
									}
								}
							}

							if ($UsersAdded -gt 0) {
								$Output += [PSCustomObject]@{
									'UserType' = $Group.UserType
									'Settings' = $Group.Settings
									#'Default' = $Group.Default
									'CollectionID' = $Group.CollectionID
									#'CollectionIDIgnoreThreshold' = $Group.CollectionIDIgnoreThreshold
									#'CollectionIDsToExclude' = $Group.CollectionIDsToExclude
									'GroupName' = $TargetSecurityGroup.DisplayName
									'Simulation' = !$MakeChanges
									'Status' = "Users Added to Group"
									'UsersModified' = $UsersAdded
									#'Description' = $Group.Description
									'Filter' = $Group.ActionFilter
									'GroupID' = $TargetSecurityGroup.objectid
									'GroupDescription' = $TargetSecurityGroup.Description
									'GroupCN' = $TargetSecurityGroup.CommonName
									'SettingsNumber' = $Group.SettingsNumber
									'SettingsDescription' = $Group.Description
								}
							}


			#$onlineusers | where { $_.UserPrincipalName -eq 'upn@school.org.uk' } | fl * << ObjectID
			#$existingMembers | where { $_.PrimarySMTPAddress -eq 'upn@school.org.uk' } | fl * << ExternalDirectoryObjectId 
			#$UsersToProcess | where { $_.EmailAddress -eq 'upn@school.org.uk' } | fl *    << ObjectID

							$PadUsersAdded=[string]$UsersAdded
							$PadUsersAdded = $PadUsersAdded.PadLeft(4," ")

							$PadUsersPendingRemoval=[string]$PendingRemovalThisGroup
							$PadUsersPendingRemoval = $PadUsersPendingRemoval.PadLeft(4," ")
				  
							if ($Verbose -eq $true) {
								write-host "$($SimString)Updated Membership - CollectionID: $PadCollectionID   $PadSettings   Group ID/Name: $PadGroupName             Added Users: $PadUsersAdded$stringb" -ForegroundColor $textcolour
								write-host "$($SimString)Updated Membership - CollectionID: $PadCollectionID   $PadSettings   Group ID/Name: $PadGroupName   Users Pending Removal: $PadUsersPendingRemoval$string" -ForegroundColor $textcolour
							}
							if ($DetailedLoggingEnabled) {
								Add-Content -Path "$LogFile" -Value "$($SimString)Updated Membership - CollectionID: $PadCollectionID   $PadSettings   Group ID/Name: $PadGroupName             Added Users: $PadUsersAdded$stringb" -Encoding Unicode
								Add-Content -Path "$LogFile" -Value "$($SimString)Updated Membership - CollectionID: $PadCollectionID   $PadSettings   Group ID/Name: $PadGroupName   Users Pending Removal: $PadUsersPendingRemoval$string" -Encoding Unicode
							}
						} else {
						
							$Filter = $null
							$OnlineUsers = @()
							if ($Error[0].Exception.Message) {
								$errorstring = $Error[0].Exception.Message
							} else {
								$errorstring = "(unknown)"
							}
												
							$Output += [PSCustomObject]@{
								'UserType' = $Group.UserType
								'Settings' = $Group.Settings
								#'Default' = $Group.Default	
								'CollectionID' = $Group.CollectionID		
								#'CollectionIDIgnoreThreshold' = $Group.CollectionIDIgnoreThreshold							
								#'CollectionIDsToExclude' = $Group.CollectionIDsToExclude
								'GroupName' = $TargetSecurityGroup.DisplayName
								'Simulation' = !$MakeChanges
								'Status' = "Unable to get $FilterType $FilterSource Users - error message: " + $errorstring 
								'UsersModified' = 0
								#'Description' = $Group.Description
								'Filter' = $Group.ActionFilter
								'GroupID' = $TargetSecurityGroup.objectid
								'GroupDescription' = $TargetSecurityGroup.Description
								'GroupCN' = $TargetSecurityGroup.CommonName
								'SettingsNumber' = $Group.SettingsNumber
								'SettingsDescription' = $Group.Description
							}
							$PadUsersNotProcessed=[string]$UsersToProcess.count
							$PadUsersNotProcessed = $PadUsersNotProcessed.PadRight(4," ")

							write-host "There was an error in retrieving $FilterType $FilterSource Users - CollectionID: $PadCollectionID   $PadSettings   Group ID/Name: $PadGroupName   Users Not Processed: $PadUsersNotProcessed" -ForegroundColor Red
							$warnings += "- There was an error in retrieving $FilterType $FilterSource Users - CollectionID: $PadCollectionID   $PadSettings   Group ID/Name: $PadGroupName   Users Not Processed: $PadUsersNotProcessed"
							if ($DetailedLoggingEnabled) {
								Add-Content -Path "$LogFile" -Value "There was an error in retrieving $FilterType $FilterSource Users - CollectionID: $PadCollectionID   $PadSettings   Group ID/Name: $PadGroupName   Users Not Processed: $PadUsersNotProcessed" -Encoding Unicode
							}
						}
					
					} else {

					# group filter means no users can be processed
						$Output += [PSCustomObject]@{
							'UserType' = $Group.UserType
							'Settings' = $Group.Settings
							#'Default' = $Group.Default
							'CollectionID' = $Group.CollectionID
							#'CollectionIDIgnoreThreshold' = $Group.CollectionIDIgnoreThreshold
							#'CollectionIDsToExclude' = $Group.CollectionIDsToExclude
							'GroupName' = $TargetSecurityGroup.DisplayName
							'Simulation' = !$MakeChanges
							'Status' = "Group Rules Impossible"
							'UsersModified' = 0
							#'Description' = $Group.Description
							'Filter' = $Group.ActionFilter
							'GroupID' = $TargetSecurityGroup.objectid
							'GroupDescription' = $TargetSecurityGroup.Description
							'GroupCN' = $TargetSecurityGroup.CommonName
							'SettingsNumber' = $Group.SettingsNumber
							'SettingsDescription' = $Group.Description
						}
				

						$PadUsersNotProcessed=[string]$UsersToProcess.count
						$PadUsersNotProcessed = $PadUsersNotProcessed.PadRight(4," ")

						write-host "Group Rules Impossible - CollectionID: $PadCollectionID   $PadSettings   Group ID/Name: $PadGroupName   Users Not Processed: $PadUsersNotProcessed" -ForegroundColor Yellow
						$warnings += "- Group Rules Impossible - CollectionID: $PadCollectionID   $PadSettings   Group ID/Name: $PadGroupName   Users Not Processed: $PadUsersNotProcessed"
						if ($DetailedLoggingEnabled) {
							Add-Content -Path "$LogFile" -Value "Group Rules Impossible - CollectionID: $PadCollectionID   $PadSettings   Group ID/Name: $PadGroupName   Users Not Processed: $PadUsersNotProcessed" -Encoding Unicode
						}
					}
                } else {
                # group filter means no users can be processed
                    $Output += [PSCustomObject]@{
                        'UserType' = $Group.UserType
						'Settings' = $Group.Settings
						#'Default' = $Group.Default
						'CollectionID' = $Group.CollectionID
						#'CollectionIDIgnoreThreshold' = $Group.CollectionIDIgnoreThreshold
						#'CollectionIDsToExclude' = $Group.CollectionIDsToExclude
                        'GroupName' = $TargetSecurityGroup.DisplayName
                        'Simulation' = !$MakeChanges
                        'Status' = "Error retrieving Group Rules Users"
                        'UsersModified' = 0
                        #'Description' = $Group.Description
                        'Filter' = $Group.ActionFilter
                        'GroupID' = $TargetSecurityGroup.objectid
                        'GroupDescription' = $TargetSecurityGroup.Description
						'GroupCN' = $TargetSecurityGroup.CommonName
						'SettingsNumber' = $Group.SettingsNumber
						'SettingsDescription' = $Group.Description
                    }
            

                    $PadUsersNotProcessed=[string]$UsersToProcess.count
                    $PadUsersNotProcessed = $PadUsersNotProcessed.PadRight(4," ")

                    write-host "Error retrieving Group Rules Users: CollectionID: $PadCollectionID   $PadSettings   Group ID/Name: $PadGroupName   Users Not Processed: $PadUsersNotProcessed" -ForegroundColor Yellow
                    $warnings += "- Error retrieving Group Rules Users: CollectionID: $PadCollectionID   $PadSettings   Group ID/Name: $PadGroupName   Users Not Processed: $PadUsersNotProcessed"
                    if ($DetailedLoggingEnabled) {
                        Add-Content -Path "$LogFile" -Value "Error retrieving Group Rules Users: CollectionID: $PadCollectionID   $PadSettings   Group ID/Name: $PadGroupName   Users Not Processed: $PadUsersNotProcessed" -Encoding Unicode
                    }
                }
# end 1.0.21

            }

            if ( @($TargetSecurityGroup).count -eq 0 -or !$TargetSecurityGroup) {
				if ( ($Group.OnFailureApplytoAllUsersOnNextRun -band 4) -eq 4 -or ($GlobalOnFailureApplytoAllUsersOnNextRun -band 2) -eq 2) { # 1.0.79
					if ($Group.UserType -eq 'Teacher') {
						$ApplytoAllTeachersOnNextRunThisFunc+=$UsersToProcess.count
					} else {
						$ApplytoAllStudentsOnNextRunThisFunc+=$UsersToProcess.count		
					}
				}
                if ($Group.GroupNameAsID -eq $true) {
                    $Output += [PSCustomObject]@{
                        'UserType' = $Group.UserType
						'Settings' = $Group.Settings
						#'Default' = $Group.Default
						'CollectionID' = $Group.CollectionID
						#'CollectionIDIgnoreThreshold' = $Group.CollectionIDIgnoreThreshold
						#'CollectionIDsToExclude' = $Group.CollectionIDsToExclude
                        'GroupName' = ''
                        'Simulation' = !$MakeChanges
                        'Status' = "Group Missing"
                        'UsersModified' = 0
                        #'Description' = $Group.Description
                        'Filter' = $Group.Filter
                        'GroupID' = $Group.GroupName
                        'GroupDescription' = ''
						'GroupCN' = ''
						'SettingsNumber' = $Group.SettingsNumber
						'SettingsDescription' = $Group.Description
                    }
                } else {
                    $Output += [PSCustomObject]@{
                        'UserType' = $Group.UserType
						'Settings' = $Group.Settings
						#'Default' = $Group.Default
						'CollectionID' = $Group.CollectionID
						#'CollectionIDIgnoreThreshold' = $Group.CollectionIDIgnoreThreshold
						#'CollectionIDsToExclude' = $Group.CollectionIDsToExclude
                        'GroupName' = $Group.GroupName
                        'Simulation' = !$MakeChanges
                        'Status' = "Group Missing"
                        'UsersModified' = 0
                        #'Description' = $Group.Description
                        'Filter' = $Group.Filter
                        'GroupID' = ''
                        'GroupDescription' = ''
						'GroupCN' = ''
						'SettingsNumber' = $Group.SettingsNumber
						'SettingsDescription' = $Group.Description
                    }
                }

                $PadUsersNotProcessed=[string]$UsersToProcess.count
                $PadUsersNotProcessed = $PadUsersNotProcessed.PadRight(4," ")

                write-host "Security Group/ID Not Found - CollectionID: $PadCollectionID   $PadSettings   Group ID/Name: $PadGroupName   Users Not Processed: $PadUsersNotProcessed" -ForegroundColor Yellow
                $warnings += "- Security Group/ID Not Found - CollectionID: $PadCollectionID   $PadSettings   Group ID/Name: $PadGroupName   Users Not Processed: $PadUsersNotProcessed"
                if ($DetailedLoggingEnabled) {
                    Add-Content -Path "$LogFile" -Value "Security Group/ID Not Found - CollectionID: $PadCollectionID   $PadSettings   Group ID/Name: $PadGroupName   Users Not Processed: $PadUsersNotProcessed" -Encoding Unicode
                }
            }
            if ( @($TargetSecurityGroup).count -gt 1) {
				if ( ($Group.OnFailureApplytoAllUsersOnNextRun -band 4) -eq 4 -or ($GlobalOnFailureApplytoAllUsersOnNextRun -band 2) -eq 2) { # 1.0.79
					if ($Group.UserType -eq 'Teacher') {
						$ApplytoAllTeachersOnNextRunThisFunc+=$UsersToProcess.count
					} else {
						$ApplytoAllStudentsOnNextRunThisFunc+=$UsersToProcess.count		
					}
				}
                if ($Group.GroupNameAsID -eq $true) {
                     $Output += [PSCustomObject]@{
                        'UserType' = $Group.UserType
						'Settings' = $Group.Settings
						#'Default' = $Group.Default
						'CollectionID' = $Group.CollectionID
						#'CollectionIDIgnoreThreshold' = $Group.CollectionIDIgnoreThreshold
						#'CollectionIDsToExclude' = $Group.CollectionIDsToExclude
                        'GroupName' = ''
                        'Simulation' = !$MakeChanges
                        'Status' = "More than 1 group with that ID"
                        'UsersModified' = 0
                        #'Description' = $Group.Description
                        'Filter' = $Group.Filter
                        'GroupID' = $Group.GroupName
                        'GroupDescription' = ''
						'GroupCN' = ''
						'SettingsNumber' = $Group.SettingsNumber
						'SettingsDescription' = $Group.Description
                    }
                } else {
                    $Output += [PSCustomObject]@{
                        'UserType' = $Group.UserType
						'Settings' = $Group.Settings
						#'Default' = $Group.Default
						'CollectionID' = $Group.CollectionID
						#'CollectionIDIgnoreThreshold' = $Group.CollectionIDIgnoreThreshold
						#'CollectionIDsToExclude' = $Group.CollectionIDsToExclude
                        'GroupName' = $Group.GroupName
                        'Simulation' = !$MakeChanges
                        'Status' = "More than 1 group with that name"
                        'UsersModified' = 0
                        #'Description' = $Group.Description
                        'Filter' = $Group.Filter
                        'GroupID' = ''
                        'GroupDescription' = ''
						'GroupCN' = ''
						'SettingsNumber' = $Group.SettingsNumber
						'SettingsDescription' = $Group.Description
                    }
                }

                $PadUsersNotProcessed=[string]$UsersToProcess.count
                $PadUsersNotProcessed = $PadUsersNotProcessed.PadRight(4," ")

                write-host "More than 1 Matching Security Group Found - CollectionID: $PadCollectionID   $PadSettings   Group ID/Name: $PadGroupName   Users Not Processed: $PadUsersNotProcessed" -ForegroundColor Yellow
                $warnings += "- More than 1 Matching Security Group Found - CollectionID: $PadCollectionID   $PadSettings   Group ID/Name: $PadGroupName   Users Not Processed: $PadUsersNotProcessed"
                if ($DetailedLoggingEnabled) {
                    Add-Content -Path "$LogFile" -Value "More than 1 Matching Security Group Found - CollectionID: $PadCollectionID   $PadSettings   Group ID/Name: $PadGroupName   Users Not Processed: $PadUsersNotProcessed" -Encoding Unicode
                }
            }
        } # end for group loop

		if ( @($PendingRemovals).Count -gt 0) {
			[int]$GroupsProcessed=0
			[int]$ResultCount=0
            $funcstarttime = (Get-Date)
			
			write-host "$($SimString)Processing Accumulated Filtered Security Group User Removals - Users Pending Removal across all Groups: $($PendingRemovals.Count)" -ForegroundColor $textcolour
			if ($DetailedLoggingEnabled) {
					Add-Content -Path "$LogFile" -Value "$($SimString)Processing Accumulated Filtered Security Group User Removals - Users Pending Removal across all Groups: $($PendingRemovals.Count)" -Encoding Unicode
            }
			[int]$TotalUsersRemoved=0
			foreach ($Group in $FilteredSecurityGroupsToProcess) {
				$GroupsProcessed = $GroupsProcessed +1
				[int]$UsersRemoved=0
				$FilteredPendingRemovals = @($PendingRemovals | where {$_.UserType -eq $Group.UserType -and $_.Settings -eq $Group.Settings -and $_.State -eq 1})
				
				if ($Group.Description) {
					$DescString = "$($Group.Settings) $($Group.Description)"
				} else {
					$DescString = "$($Group.Settings)"
				}

				$ProcessedString = [string]$GroupsProcessed
				$ProcessedString = $ProcessedString.PadLeft(($FilteredSecurityGroupsToProcess.Count -as [string]).length," ")

				if ( $FilteredPendingRemovals.Count -gt 0) {

					$string = $FilteredPendingRemovals[0].EmailAddress
					$string = ", including: $string"
				} else {
					$string = ''
				}
				$PadGroupName=$Group.GroupName.PadRight($GroupGroupNameMaxLength," ")
				$PadSettings=$Group.Settings.PadRight($SettingsMaxLength," ")
				$PadCollectionID=([string]$Group.CollectionID).PadRight($CollectionIDMaxLength," ")
				
				$PadUsersPendingRemoval=[string]$FilteredPendingRemovals.Count
                $PadUsersPendingRemoval = $PadUsersPendingRemoval.PadLeft(4," ")

				write-host "$($SimString)Processing Group $ProcessedString of $($FilteredSecurityGroupsToProcess.Count): CollectionID: $PadCollectionID   $PadSettings   Group ID/Name: $PadGroupName   Users Being Removed: $PadUsersPendingRemoval$string" -ForegroundColor $textcolour

				if ($DetailedLoggingEnabled) {
					Add-Content -Path "$LogFile" -Value "$($SimString)Processing Group $ProcessedString of $($FilteredSecurityGroupsToProcess.Count): CollectionID: $PadCollectionID   $PadSettings   Group ID/Name: $PadGroupName   Users Being Removed: $PadUsersPendingRemoval$string" -Encoding Unicode
				}

				if ( $FilteredPendingRemovals.Count -gt 0) {
					
					foreach ($PendingUser in $FilteredPendingRemovals) {
						$ResultCount = $ResultCount + 1
						$UsersRemoved+=1

						$ErrorGenerated=$false
						$errorstring = ""
						if ($MakeChanges -eq $true) {
							$e = $error.count
							Remove-MsolGroupMember -GroupObjectId $PendingUser.GroupID -GroupMemberType User -GroupmemberObjectId $PendingUser.UserID -ErrorAction SilentlyContinue
							if ($error.count -gt $e) {
								if ($Error[0].Exception.Message) {
									$errorstring = $Error[0].Exception.Message
								} else {
									$errorstring = "(unknown)"
								}
							$ErrorGenerated=$true
							}
						} 

						$UserOutput += [PSCustomObject]@{
							'UserType' = $PendingUser.UserType
							'Settings' = $PendingUser.Settings
							#'Default' = $Group.Default
							'CollectionID' = $PendingUser.CollectionID
							#'CollectionIDIgnoreThreshold' = $PendingUser.CollectionIDIgnoreThreshold
							'DisplayName' = $PendingUser.DisplayName
							'EmailAddress' = $PendingUser.EmailAddress
							'Action' = "Removed From Group"
							'GroupName' = $PendingUser.GroupName
							'UserID' = $PendingUser.UserID
							'GroupID' = $PendingUser.GroupID
							'GroupDescription' = $PendingUser.GroupDescription
							'GroupCN' = $PendingUser.GroupCN
							'Simulation' = !$MakeChanges
							'Filter' = $PendingUser.Filter
							'NumberofFilteredUsers' = $PendingUser.NumberofFilteredUsers
							'RemoveUnMatchedMembers' = $PendingUser.RemoveUnMatchedMembers
							'OnlyRemoveSameType' = $PendingUser.OnlyRemoveSameType
							'SettingsNumber' = $PendingUser.SettingsNumber
							'SettingsDescription' = $PendingUser.SettingsDescription
							'State' = 1
							'ErrorGenerated' = $ErrorGenerated
							'ErrorMessage' = $errorstring
						}
		
						if ($ResultCount -eq $LongJobUpdateInterval) {
							[int]$ResultCount=0
							
							$UsersProcessedString, $timestring, $timeremainingseconds = get-timeelapsed -StartTime $funcstarttime -Processed ($TotalUsersRemoved+$UsersRemoved) -ToProcess ($PendingRemovals.Count)
							
							if ($timeremainingseconds -ge 10) {

								write-host "$($simstring)Processing Accumulated Filtered Security Group User Removals - Processed $UsersProcessedString of $($PendingRemovals.Count) users - ETR$timestring" -ForegroundColor Gray

								if ($DetailedLoggingEnabled) {
									Add-Content -Path "$LogFile" -Value "$($simstring)Processing Accumulated Filtered Security Group User Removals - Processed $UsersProcessedString of $($UsersToApply.Count) users - ETR$timestring" -Encoding Unicode  
								}
							}
						}						
					}

					$Output += [PSCustomObject]@{
						'UserType' = $Group.UserType
						'Settings' = $Group.Settings
						#'Default' = $Group.Default
						'CollectionID' = $Group.CollectionID
						#'CollectionIDIgnoreThreshold' = $Group.CollectionIDIgnoreThreshold
						#'CollectionIDsToExclude' = $Group.CollectionIDsToExclude
						'GroupName' = $FilteredPendingRemovals[0].GroupName
						'Simulation' = !$MakeChanges
						'Status' = "Users Removed From Group"
						'UsersModified' = $UsersRemoved
						#'Description' = $Group.Description
						'Filter' = $Group.ActionFilter
						'GroupID' = $FilteredPendingRemovals[0].GroupID
						'GroupDescription' = $FilteredPendingRemovals[0].GroupDescription
						'GroupCN' = $FilteredPendingRemovals[0].GroupCN
						'SettingsNumber' = $FilteredPendingRemovals[0].SettingsNumber
						'SettingsDescription' = $FilteredPendingRemovals[0].SettingsDescription
					}
				$TotalUsersRemoved+=$UsersRemoved
				}	
			}
			
			if ($TotalUsersRemoved -ne @($PendingRemovals).Count) {
				write-host "Filtered Security Group Processing Error: Total Users Removed: $UsersRemoved - Total Pending Removal: $($PendingRemovals.Count)" -ForegroundColor Red
				$MajorErrors += "- Filtered Security Group Processing Error: Total Users Removed: $UsersRemoved - Total Pending Removal: $($PendingRemovals.Count)"
				if ($DetailedLoggingEnabled) {
                    Add-Content -Path "$LogFile" -Value "Filtered Security Group Processing Error: Total Users Removed: $UsersRemoved - Total Pending Removal: $($PendingRemovals.Count)" -Encoding Unicode
                }
			}
		} else {
			write-host "$($SimString)Processing Accumulated Filtered Security Group User Removals - Users Pending Removal across all Groups: $($PendingRemovals.Count)" -ForegroundColor $textcolour
			if ($DetailedLoggingEnabled) {
                    Add-Content -Path "$LogFile" -Value "$($SimString)Processing Accumulated Filtered Security Group User Removals - Users Pending Removal across all Groups: $($PendingRemovals.Count)" -Encoding Unicode
            }	
		}

		$UserOutput = @($UserOutput | Sort @{Expression = "UserType"; Descending = $true},@{Expression = "CollectionID"; Descending = $false},@{Expression = "SettingsNumber"; Descending = $false},@{Expression = "State"; Descending = $false},@{Expression = "EmailAddress"; Descending = $false})
		$Output = @($Output | Sort @{Expression = "UserType"; Descending = $true},@{Expression = "CollectionID"; Descending = $false},@{Expression = "SettingsNumber"; Descending = $false})

        $TeacherGroupsProcessed = @($Output | where { $_.UserType -eq 'Teacher' })
        $TeacherQty = ($TeacherGroupsProcessed.usersmodified | Measure-Object -sum).sum
        $TeacherGroups = @($FilteredSecurityGroupsToProcess | where {$_.UserType -eq 'Teacher' }).count

        $results += [PSCustomObject]@{
            'Type' = "Filtered Security Group Processing - $($TeacherGroupsProcessed.Count) Teacher Security Groups"
		    'Quantity' = $TeacherQty
		    'Source' = "$TeacherGroups Groups"
            'Action' = if ($TeacherQty -gt 0) { "$($SimString)Group Membership Updated" } else { "$($SimString)Group Membership Checked" }
            'Target' = 'Teachers'
            'OtherInfo' = ''
            'AssignmentErrors' = @($UserOutput  | where { $_.UserType -eq 'Teacher' -and $ErrorGenerated -eq $true }).count
             }

        $StudentGroupsProcessed = @($Output | where { $_.UserType -eq 'Student' })
        $StudentQty = ($StudentGroupsProcessed.usersmodified | Measure-Object -sum).sum
        $StudentGroups = @($FilteredSecurityGroupsToProcess | where {$_.UserType -eq 'Student' }).count

        $results += [PSCustomObject]@{
            'Type' = "Filtered Security Group Processing - $($StudentGroupsProcessed.Count) Student Security Groups"
		    'Quantity' = $StudentQty
		    'Source' = "$StudentGroups Groups"
            'Action' = if ($StudentQty -gt 0) { "$($SimString)Group Membership Updated" } else { "$($SimString)Group Membership Checked" }
            'Target' = 'Students'
            'OtherInfo' = ''
            'AssignmentErrors' = @($UserOutput  | where { $_.UserType -eq 'Student' -and $ErrorGenerated -eq $true }).count
             }


        if ($DetailedLoggingEnabled) {
            Add-Content -Path "$LogFile" -Value "`r`nFiltered Security Group Results:" -Encoding Unicode
            Add-Content -Path "$LogFile" -Value "--------------------------------" -Encoding Unicode
        
            Process-UserCSVOutput ($UserOutput)
            $littleobj = @()
			$littleobj = @($UserOutput | where { $_.Action -eq 'Removed From Group' -or $_.Action -eq 'Added To Group'})
            Add-Content -Path "$LogFile" -Value "$($SimString)$($UserOutput.count) Filtered Security Group Membership Test operations with $($littleobj.count) Total User Membership Changes" -Encoding Unicode   
			$littleobj = @($littleobj  | Sort-Object -Property ObjectID -Unique) # 1.0.94
			Add-Content -Path "$LogFile" -Value "$($littleobj.count) Unique Users that required User Membership Changes`r`n" -Encoding Unicode

			$DefaultUserOutput = @($UserOutput | where { $_.Default -eq $true})
			$littleobj = @($DefaultUserOutput | where { $_.Action -eq 'Removed From Group' -or $_.Action -eq 'Added To Group'})
			Add-Content -Path "$LogFile" -Value "$($SimString)$($DefaultUserOutput.count) Filtered Security Group Add/Remove operations with $($littleobj.count) Total User Membership Changes under Default Processing" -Encoding Unicode
			$littleobj = @($littleobj  | Sort-Object -Property ObjectID -Unique) # 1.0.94
			Add-Content -Path "$LogFile" -Value "$($littleobj.count) Unique Users that required User Membership Changes under Default Processing`r`n" -Encoding Unicode
        
            Add-Content -Path "$LogFile" -Value "Group Results:" -Encoding Unicode
            Add-Content -Path "$LogFile" -Value "--------------" -Encoding Unicode
            $Output | ft * -AutoSize | Out-String -Width 4096 | Out-File -filepath "$LogFile" -append -Encoding Unicode
            Add-Content -Path "$LogFile" -Value "$($SimString)$($Output.Count) Filtered Security Group Add/Remove Batch Requests Processed with $TeacherQty Teacher Group Membership Changes and $StudentQty Student Group Membership Changes`r`n" -Encoding Unicode   


        }
        if ($MakeChanges -eq $true ) {
            $Actions += "- $($Output.Count) Filtered Security Group Add/Remove Batch Requests Processed with $TeacherQty Teacher Group Membership Changes and $StudentQty Student Group Membership Changes"
            write-host "$($Output.Count) Filtered Security Group Add/Remove Batch Requests Processed with $TeacherQty Teacher Group Membership Changes and $StudentQty Student Group Membership Changes" -ForegroundColor Yellow

            $TotalErrors = ($UserOutput | where { $_.ErrorGenerated -eq $true}).Count

            if ( $TotalErrors -gt 0) {
                if ($DetailedLoggingEnabled) {
			        Add-Content -Path "$LogFile" -Value "`r`nAdd/Remove Errors:" -Encoding Unicode
			        Add-Content -Path "$LogFile" -Value "------------------`r`n" -Encoding Unicode
    			    Add-Content -Path "$LogFile" -Value "$TotalErrors Group Add/Remove Errors were encountered during Filtered Security Group processing" -Encoding Unicode
                }
			    write-host "$TotalErrors Group Add/Remove Errors were encountered during Filtered Security Group processing" -ForegroundColor Red
			    $warnings += "- $TotalErrors Group Add/Remove Errors were encountered during Filtered Security Group processing"
		    }

        } else {
            $Actions += "- Simulation: $($Output.Count) Filtered Security Group Add/Remove Batch Requests Processed with $TeacherQty Teacher Group Membership Changes and $StudentQty Student Group Membership Changes"
            write-host "Simulation: $($Output.Count) Filtered Security Group Add/Remove Batch Requests Processed with $TeacherQty Teacher Group Membership Changes and $StudentQty Student Group Membership Changes" -ForegroundColor cyan
        }


		if ($ApplytoAllTeachersOnNextRunThisFunc -gt 0 -or $ApplytoAllStudentsOnNextRunThisFunc -gt 0) {
			$ApplytoAllonNextRunSummary += [PSCustomObject]@{
				'Function' = 'Filtered Security Group Processing'
				'Teachers' = $ApplytoAllTeachersOnNextRunThisFunc 
				'Students' = $ApplytoAllStudentsOnNextRunThisFunc
			}
		}
		if ($ApplytoAllTeachersOnNextRunThisFunc -gt 0) { $ApplytoAllTeachersOnNextRun=$true }
		if ($ApplytoAllStudentsOnNextRunThisFunc -gt 0) { $ApplytoAllStudentsOnNextRun=$true }


    } else {
        write-host "There are no Filtered Security Groups to Apply" -foregroundcolor white
        $Actions += "- There are no Filtered Security Groups to Apply"
        if ($DetailedLoggingEnabled) {
            Add-Content -Path "$LogFile" -Value "`r`nApplying Filtered Security Groups" -Encoding Unicode
            Add-Content -Path "$LogFile" -Value "=================================`r`n" -Encoding Unicode
            Add-Content -Path "$LogFile" -Value "Group Changes to Process:" -Encoding Unicode
            Add-Content -Path "$LogFile" -Value "-------------------------" -Encoding Unicode
            Add-Content -Path "$LogFile" -Value "`r`nThere are no Filtered Security Groups to Apply" -Encoding Unicode
        }
    }
} else {
	if ($DetailedLoggingEnabled) {
		Add-Content -Path "$LogFile" -Value "`r`nFiltered Security Groups" -Encoding Unicode
		Add-Content -Path "$LogFile" -Value "========================`r`n" -Encoding Unicode
        Add-Content -Path "$LogFile" -Value "Group Changes that would be Processed:" -Encoding Unicode
		Add-Content -Path "$LogFile" -Value "--------------------------------------" -Encoding Unicode
		if ($FilteredSecurityGroupsToProcess.Count -gt 0) {
			$FilteredSecurityGroupsToProcess | select -Property * -ExcludeProperty CollectionIDsToExclude | ft * -AutoSize | Out-String -Width 4096 | Out-File -filepath "$LogFile" -append -Encoding Unicode # 1.0.94
			Add-Content -Path "$LogFile" -Value "Note: If the global filter type setting is set to Remote, the filter source is forced to CSOnline`r`n" -Encoding Unicode
		} else {
			Add-Content -Path "$LogFile" -Value "`r`nThere are no Filtered Security Groups to Apply" -Encoding Unicode
		}
	}	
}



# process filtered Unified Groups
if ($MajorErrors.Count -eq 0) {
	[int]$ApplytoAllTeachersOnNextRunThisFunc=0
	[int]$ApplytoAllStudentsOnNextRunThisFunc=0
    if ($FilteredUnifiedGroupsToProcess.Count -gt 0) {
        write-host "Applying Filtered Unified Groups" -foregroundcolor green        
        if ($DetailedLoggingEnabled) {
            Add-Content -Path "$LogFile" -Value "`r`nApplying Filtered Unified Groups" -Encoding Unicode
            Add-Content -Path "$LogFile" -Value "================================`r`n" -Encoding Unicode
            Add-Content -Path "$LogFile" -Value "Group Changes to Process:" -Encoding Unicode
            Add-Content -Path "$LogFile" -Value "-------------------------" -Encoding Unicode
            $FilteredUnifiedGroupsToProcess | select -Property * -ExcludeProperty CollectionIDsToExclude | ft * -AutoSize | Out-String -Width 4096 | Out-File -filepath "$LogFile" -append -Encoding Unicode # 1.0.94
			Add-Content -Path "$LogFile" -Value "Note: If the global filter type setting is set to Remote, the filter source is forced to CSOnline`r`n" -Encoding Unicode
        }
        [int]$GroupsProcessed=0
        $Filter=$null
		$FilterSource=$null
        $OnlineUsers= @()
		$UnfilteredOnlineUsers = @()
        $UserOutput = @()
		$PendingRemovals = @()
		$FilteredPendingRemovals = @()
        $Output = @()

        [int]$GroupGroupNameMaxLength=($FilteredUnifiedGroupsToProcess | Select GroupName | Sort -Property { $_.GroupName.length} | select -Last 1).GroupName.Length
		[int]$SettingsMaxLength=($FilteredUnifiedGroupsToProcess | Select Settings | Sort -Property { $_.Settings.length} | select -Last 1).Settings.Length
		[int]$CollectionIDMaxLength=(($FilteredUnifiedGroupsToProcess | Select CollectionID | Sort -Property { $_.CollectionID} | select -Last 1).CollectionID -as [string]).length
        if ( @($FilteredUnifiedGroupsToProcess | where { $_.RemoveUnMatchedMembers -gt 0 }).count -gt 0) {
            $HasRemoveUnMatchedMembers = $true
        } else {
            $HasRemoveUnMatchedMembers = $false
        }

        if ( @($FilteredUnifiedGroupsToProcess | where { $_.RemoveUnMatchedMembers -gt 0 -and $_.OnlyRemoveSameType -eq $false }).count -gt 0) {
            $OnlyRemoveSameTypemaxLength = 5
        } else {
            $OnlyRemoveSameTypemaxLength = 4
        }

        $AzureUsers = $null

        foreach ($Group in $FilteredUnifiedGroupsToProcess) {
            [int]$UsersAdded=0
			
            [int]$PendingRemovalThisGroup=0
			
            [string]$string = ''
            [string]$stringb = ''

            $GroupsProcessed = $GroupsProcessed +1

            if ($Group.Description) {
                $DescString = "$($Group.Description)"
            } else {
                $DescString = "-"
            }

            $ProcessedString = [string]$GroupsProcessed
			$ProcessedString = $ProcessedString.PadLeft(($FilteredUnifiedGroupsToProcess.Count -as [string]).length," ")

            $PadGroupName=$Group.GroupName.PadRight($GroupGroupNameMaxLength," ")

			$PadSettings=$Group.Settings.PadRight($SettingsMaxLength," ")
			$PadCollectionID=([string]$Group.CollectionID).PadRight($CollectionIDMaxLength," ")
            #$PadUnMatchedMembers = [string]$($Group.RemoveUnMatchedMembers)
            #$PadUnMatchedMembers = $PadUnMatchedMembers.PadRight($RemoveUnMatchedMembersmaxLength," ")

            if ($Group.RemoveUnMatchedMembers -gt 0) {
				$UnMatchedMembers = ""
				if (($Group.RemoveUnMatchedMembers -band 2) -eq 2)  { $UnMatchedMembers = "A" }
				if (($Group.RemoveUnMatchedMembers -band 4) -eq 4)  { $UnMatchedMembers = "$($UnMatchedMembers)G" }
				if (($Group.RemoveUnMatchedMembers -band 8) -eq 8)  { $UnMatchedMembers = "$($UnMatchedMembers)O" }
				if (($Group.RemoveUnMatchedMembers -band 16) -eq 16)  { $UnMatchedMembers = "$($UnMatchedMembers)C" }
				$PadUnMatchedMembers = $UnMatchedMembers.PadRight(4," ")
                $PadOnlyRemoveSameType = [string]$($Group.OnlyRemoveSameType)
                $PadOnlyRemoveSameType = $PadOnlyRemoveSameType.PadRight($OnlyRemoveSameTypemaxLength," ")
                $PadOnlyRemoveSameType = "- $($Group.UserType)s Only: $PadOnlyRemoveSameType "
            } else {
                $PadUnMatchedMembers = "    "
				$UnMatchedMembers = $false
				$PadOnlyRemoveSameType=""
                if ($HasRemoveUnMatchedMembers -eq $true) {
                    $PadOnlyRemoveSameType = $PadOnlyRemoveSameType.PadRight($OnlyRemoveSameTypemaxLength," ")
                    $PadOnlyRemoveSameType = "                $PadOnlyRemoveSameType "
                }
            }
            
            write-host "$($SimString)Processing Group $ProcessedString of $($FilteredUnifiedGroupsToProcess.Count): CollectionID: $PadCollectionID   $PadSettings   Group ID/Name: $PadGroupName   Remove UnMatched Members: $PadUnMatchedMembers $PadOnlyRemoveSameType   Desc: $DescString" -ForegroundColor $textcolour
            
            if ($DetailedLoggingEnabled) {
                Add-Content -Path "$LogFile" -Value "$($SimString)Processing Group $ProcessedString of $($FilteredUnifiedGroupsToProcess.Count):  CollectionID: $PadCollectionID   $PadSettings   Group ID/Name: $PadGroupName   Remove UnMatched Members: $PadUnMatchedMembers $PadOnlyRemoveSameType   Desc: $DescString" -Encoding Unicode
            }

			$string=$null
			$string=""
            if ($Group.UserType -eq 'Student') {
                if ($Group.ApplyToAll -eq $true -or $ApplytoAllStudents -eq $true) {
                    $UsersToProcess = $StudentList
                } else {
                    $UsersToProcess = $StudentsNotInLicensingGroup
					$string=" that were not members of the Licensing Group"
                }
            }
            if ($Group.UserType -eq 'Teacher') {
				if ($Group.ApplyToAll -eq $true -or $ApplytoAllTeachers -eq $true) {
                    $UsersToProcess = $TeacherList
                } else {
                    $UsersToProcess = $TeachersNotInLicensingGroup
					$string=" that were not members of the Licensing Group"
                }
            }

			if ($Group.DomainFilter ) {
				$UsersToProcess = @($UsersToProcess | where { $Group.DomainFilter -contains $_.EmailAddress.Split('@')[1] })
				$string="$string and were filtered by the Domain Filter"
			}

            if ($Group.OnlyApplyToUnlicensed -eq $true) {
                $UsersToProcess = @($UsersToProcess | where { $_.IsLicensed -eq $false })
				write-host "$($SimString)Using $($UsersToProcess.count) Unlicensed Source List $($Group.UserType)s$String" -ForegroundColor $textcolour	
				if ($DetailedLoggingEnabled) {
					Add-Content -Path "$LogFile" -Value "$($SimString)Using $($UsersToProcess.count) Unlicensed Source List $($Group.UserType)s$String"
				}				
            } else {
				write-host "$($SimString)Using $($UsersToProcess.count) Source List $($Group.UserType)s$String" -ForegroundColor $textcolour	
				if ($DetailedLoggingEnabled) {
					Add-Content -Path "$LogFile" -Value "$($SimString)Using $($UsersToProcess.count) Source List $($Group.UserType)s$String"
				}	
			}
			$string=$null

            $RequiredRecursiveIDs = @()
            $NotRequiredRecursiveIDs = @()
            $ProblemGroups = @()
            $GroupRulesOK = $true
			$GotGroupUsersOK = $true
			$GroupMatchedUsers = @()
            if ($Group.HasGroupRules -eq $true ) {

                write-host "$($SimString)Building Group Rules Table" -ForegroundColor $textcolour
                
                if ($DetailedLoggingEnabled) {
                    Add-Content -Path "$LogFile" -Value "`r`n$($SimString)Building Group Rules Table`r`n" -Encoding Unicode
                }
				$e = $error.count
                $GroupRulesOK, $ProblemGroups, $RequiredRecursiveIDs, $RequiredORRecursiveIDs, $NotRequiredRecursiveIDs = BuildGroupTable -GroupInformation $Group

                if ($GroupRulesOK -eq $true) {
                    write-host "$($SimString)Group Rules OK: $GroupRulesOK   Problem Groups: $($ProblemGroups.count)   RequiredGroups: $($RequiredRecursiveIDs.count)   RequiredOrGroups: $($RequiredORRecursiveIDs.count)   NotRequiredGroups: $($NotRequiredRecursiveIDs.count)" -ForegroundColor $textcolour
					$GroupMatchedUsers = GetGroupRuleUsers -RequiredRecursiveIDs $RequiredRecursiveIDs -RequiredORRecursiveIDs $RequiredORRecursiveIDs -NotRequiredRecursiveIDs $NotRequiredRecursiveIDs
					write-host "$($SimString)Found $($GroupMatchedUsers.count) users matching the Group Rules" -ForegroundColor $textcolour
                } else {
                    write-host "$($SimString)Group Rules OK: $GroupRulesOK   Problem Groups: $($ProblemGroups.count)   RequiredGroups: $($RequiredRecursiveIDs.count)   RequiredOrGroups: $($RequiredORRecursiveIDs.count)   NotRequiredGroups: $($NotRequiredRecursiveIDs.count)" -ForegroundColor yellow
                    $warnings += "- $($SimString)Filtered Unified Group $($GroupsProcessed): Group Rules OK: $GroupRulesOK   Problem Groups: $($ProblemGroups.count)   RequiredGroups: $($RequiredRecursiveIDs.count)   RequiredOrGroups: $($RequiredORRecursiveIDs.count)   NotRequiredGroups: $($NotRequiredRecursiveIDs.count)"

                }
                if ($error.count -gt $e) {
                    $errorstring = ""
			        if ($Error[0].Exception.Message) {
				        $errorstring = $Error[0].Exception.Message
			        } else {
				        $errorstring = "(unknown)"
			        }
					write-host "There was an error in processing Group Rules or matching users - error message: $errorstring" -foreground red
					if ($DetailedLoggingEnabled) {
						Add-Content -Path "$LogFile" -Value "There was an error in processing Group Rules or matching users - error message: $errorstring" -Encoding Unicode
					}
					$Warnings += "- $($SimString)Filtered Unified Group $($GroupsProcessed): There was an error in processing Group Rules or matching users - error message: $errorstring"
					$GotGroupUsersOK = $false
                }

                if ($DetailedLoggingEnabled) {
                    if ($RequiredRecursiveIDs.count -gt 0) {
						Add-Content -Path "$LogFile" -Value "Group Rules: RequiredGroups, Including Recursive"
                        Add-Content -Path "$LogFile" -Value "------------------------------------------------"
                        $RequiredRecursiveIDs | Sort DisplayName,  CommonName, Description, ObjectID | fl * | Out-String -Width 4096 | Out-File -filepath "$LogFile" -append -Encoding Unicode
                    }
                    if ($RequiredORRecursiveIDs.count -gt 0) {
						Add-Content -Path "$LogFile" -Value "Group Rules: RequiredOrGroups, Including Recursive"
                        Add-Content -Path "$LogFile" -Value "--------------------------------------------------"
                        $RequiredORRecursiveIDs | Sort DisplayName,  CommonName, Description, ObjectID | fl * | Out-String -Width 4096 | Out-File -filepath "$LogFile" -append -Encoding Unicode
                    }					
                    if ($NotRequiredRecursiveIDs.count -gt 0) {
						Add-Content -Path "$LogFile" -Value "Group Rules: NotRequiredGroups, Including Recursive"
                        Add-Content -Path "$LogFile" -Value "---------------------------------------------------"
                        $NotRequiredRecursiveIDs | Sort DisplayName,  CommonName, Description, ObjectID | fl * | Out-String -Width 4096 | Out-File -filepath "$LogFile" -append -Encoding Unicode
                    }
                    if ($ProblemGroups.count -gt 0) {
                        Add-Content -Path "$LogFile" -Value "Group Rules: Problem Groups"
                        Add-Content -Path "$LogFile" -Value "---------------------------"
                        $ProblemGroups | Sort DisplayName,  CommonName, Description, ObjectID | fl * | Out-String -Width 4096 | Out-File -filepath "$LogFile" -append -Encoding Unicode
                    }
                    Add-Content -Path "$LogFile" -Value "$($SimString)Group Rules OK: $GroupRulesOK   Problem Groups: $($ProblemGroups.count)   RequiredGroups: $($RequiredRecursiveIDs.count)   RequiredOrGroups: $($RequiredORRecursiveIDs.count)   NotRequiredGroups: $($NotRequiredRecursiveIDs.count)" -Encoding Unicode
					Add-Content -Path "$LogFile" -Value "$($SimString)Found $($GroupMatchedUsers.count) users matching the Group Rules" -Encoding Unicode
                }
            }
# end 1.0.21

			if ($Group.HasADOURules -eq $true ) {
					write-host "$($SimString)Active Directory OU/CN Rules - Included OU/CN Or Rules: $($Group.RequiredOrOUs.count)   Not Required OU/CN Rules: $($Group.NotRequiredOUs.count)" -ForegroundColor $textcolour	
				
				if ($DetailedLoggingEnabled) {
					Add-Content -Path "$LogFile" -Value "`r`nActive Directory OU/CN Rules"
                    Add-Content -Path "$LogFile" -Value "----------------------------"
					$Group | select RequiredOrOUs, NotRequiredOUs | Sort RequiredOrOUs, NotRequiredOUs | ft * -AutoSize | Out-String -Width 4096 | Out-File -filepath "$LogFile" -append -Encoding Unicode
	
					Add-Content -Path "$LogFile" -Value "$($SimString)Included OU/CN Or Rules: $($Group.RequiredOrOUs.count)   Not Required OU/CN Rules: $($Group.NotRequiredOUs.count)"
				}
			}

            #$TargetUnifiedGroup = get-UnifiedGroup -Identity $Group.GroupName -ErrorAction SilentlyContinue -WarningAction SilentlyContinue

            $TargetUnifiedGroup = $null
            if ($Group.GroupNameAsID -eq $true) {
                $TargetUnifiedGroup = @(get-UnifiedGroup -Identity $Group.GroupName -ErrorAction SilentlyContinue -WarningAction SilentlyContinue)
                if ($Group.DisableNameOrIDChecking -eq $false) {
                    $TargetUnifiedGroup = $TargetUnifiedGroup | where { $_.Id -like $Group.GroupName -or $_.ExternalDirectoryObjectID -like $Group.GroupName -or $_.GUID -like $Group.GroupName } # 1.0.7
                }
            } else {
                $TargetUnifiedGroup = @(get-UnifiedGroup -Identity $Group.GroupName -ErrorAction SilentlyContinue -WarningAction SilentlyContinue)
                if ($Group.DisableNameOrIDChecking -eq $false) {
                    $TargetUnifiedGroup = $TargetUnifiedGroup | where { $_.Name -like $Group.GroupName -or $_.DisplayName -like $Group.GroupName -or $_.Alias -like $Group.GroupName -or $_.PrimarySMTPAddress -like $Group.GroupName } # 1.0.7
                }
            }  # code is actually the same for both

            if ( @($TargetUnifiedGroup).count -eq 1) {
                if ($GotGroupUsersOK -eq $true) {
					if ($GroupRulesOK -eq $true) {
						$e = $error.count

						if ($Group.HasADOURules -eq $true -and !$AzureUsers) {
							$AzureUsers = @(AzureADPreview\Get-AzureADUser -all $true -ErrorAction SilentlyContinue -WarningAction SilentlyContinue)
						}

						# Get All Filtered Online Users
						if ($Group.ActionFilter) {
							$FilteringEnabled = $true
							if ($Filter -ne $Group.ActionFilter -or $FilterSource -ne $Group.FilterSource) {
								$Filter = $Group.ActionFilter
								$FilterSource = $Group.FilterSource
								
								if ($UseLocalAttributeFiltering -eq $false) {
									$OnlineUsers = @(Get-CSOnlineUser -Filter $Filter -ErrorAction SilentlyContinue -WarningAction SilentlyContinue)
									$ObjectField = 'Identity'
								} else {
									if ($FilterSource -eq 'AzureAD') {
										if (!$UnfilteredOnlineUsers) {
											if ($AzureUsers) {
												$UnfilteredOnlineUsers = @($AzureUsers)
											} else {
												$UnfilteredOnlineUsers = @(AzureADPreview\Get-AzureADUser -all $true -ErrorAction SilentlyContinue -WarningAction SilentlyContinue)
											}
										}
										$OnlineUsers = @($UnfilteredOnlineUsers | where { Invoke-Expression $Filter })	
										$ObjectField = 'ObjectID'
									} else {
										if (!$UnfilteredOnlineUsers) {
											$UnfilteredOnlineUsers = @(Get-CSOnlineUser -ErrorAction SilentlyContinue -WarningAction SilentlyContinue)
										}
										$OnlineUsers = @($UnfilteredOnlineUsers | where { Invoke-Expression $Filter })
										$ObjectField = 'Identity'
									}
								}
							}
						} else {
							$FilteringEnabled = $false
							#if ($Filter -or $OnlineUsers.count -eq 0) {
								#$Filter = $null
								#$FilterSource = $Group.FilterSource
								#if ($UseLocalAttributeFiltering -eq $false) {
									#$OnlineUsers = @(Get-CSOnlineUser -ErrorAction SilentlyContinue -WarningAction SilentlyContinue)
								#} else {
									#if ($FilterSource -eq 'AzureAD') {
										#if (!$UnfilteredOnlineUsers) {
											#$UnfilteredOnlineUsers = @(AzureADPreview\Get-AzureADUser -all $true -ErrorAction SilentlyContinue -WarningAction SilentlyContinue)
										#}
										#$OnlineUsers = @($UnfilteredOnlineUsers)
									#} else {
										#if (!$UnfilteredOnlineUsers) {
											#$UnfilteredOnlineUsers = @(Get-CSOnlineUser -ErrorAction SilentlyContinue -WarningAction SilentlyContinue)
										#}
										#$OnlineUsers = @($UnfilteredOnlineUsers)
									#}
								#}
							#}
						}

						if ($error.count -le $e) {
							# Get Existing Group Members
					
							$ExistingMembers = $null
							#$ExistingMembers = @(get-UnifiedGroupLinks -Identity $Group.GroupName -LinkType Members -ErrorAction SilentlyContinue -WarningAction SilentlyContinue)
							$ExistingMembers = @(get-UnifiedGroupLinks -Identity $TargetUnifiedGroup.guid.guid -LinkType Members -ErrorAction SilentlyContinue -WarningAction SilentlyContinue) # 1.0.60
							$ExistingMembers = @( $ExistingMembers | Sort PrimarySMTPAddress ) # 1.0.61
					
							if ($Group.RemoveUnMatchedMembers -gt 0) {
								if ($Group.UserType -eq 'Teacher') {
									$BaseUserGroup = $Teacherlist
								} else {
									$BaseUserGroup = $Studentlist
								}

								[int]$ResultCount=0
								[int]$personprocessed=0
								$funcstarttime = (Get-Date)
								
								write-host "$($simstring)Processing Filtered Unified Group Existing Members - CollectionID: $PadCollectionID   $PadSettings   Group ID/Name: $PadGroupName   Users to Process: $($ExistingMembers.Count)" -ForegroundColor $textcolour

								if ($DetailedLoggingEnabled) {
									Add-Content -Path "$LogFile" -Value "$($simstring)Processing Filtered Unified Group Existing Members - CollectionID: $PadCollectionID   $PadSettings   Group ID/Name: $PadGroupName   Users to Process: $($ExistingMembers.Count)" -Encoding Unicode  
								}
								
								foreach ($ExistingMember in $ExistingMembers) { # compare existing members against csonlineusers (filtered), but make sure that user type is matched
									$ResultCount = $ResultCount + 1
									$personprocessed = $personprocessed + 1
									
									$AddressToUse = $null
									$AddressToUse = $ExistingMember.PrimarySMTPAddress # 1.0.82
									If ($AddressToUse -eq $null -or $AddressToUse -eq "" ) { $AddressToUse = $ExistingMember.WindowsLiveID } # 1.0.82
									
									
									#if ($Group.OnlyRemoveSameType -eq $false -or $BaseUserGroup.EmailAddress -contains $AddressToUse) { # 1.0.82
									if ($Group.OnlyRemoveSameType -eq $false -or $BaseUserGroup.ObjectID -contains $ExistingMember.ExternalDirectoryObjectId) { # 1.0.85
										
										$OU = $null
										$OU = ($AzureUsers | where {$_.ObjectID -eq $ExistingMember.ExternalDirectoryObjectId} | Select -First 1).ExtensionProperty.onPremisesDistinguishedName
										
										if ( ($FilteringEnabled -eq $true -and (($Group.RemoveUnMatchedMembers -band 2) -eq 2) -and $OnlineUsers.($ObjectField) -notcontains $ExistingMember.ExternalDirectoryObjectId ) -or ($Group.HasGroupRules -eq $true -and (($Group.RemoveUnMatchedMembers -band 4) -eq 4) -and ($GroupMatchedUsers.ObjectID -notcontains $ExistingMember.ExternalDirectoryObjectId)) -or ($Group.HasADOURules -eq $true -and (($Group.RemoveUnMatchedMembers -band 8) -eq 8) -and $OU -and (Check_ADCanonicalName -UserCN $OU -RequiredOrOUs $Group.RequiredOrOUs -NotRequiredOUs $Group.NotRequiredOUs) -ne $true) -or ( (($Group.RemoveUnMatchedMembers -band 16) -eq 16) -and @($UserOutput | where {$_.CollectionID -gt $Group.CollectionIDIgnoreThreshold -and $_.CollectionID -lt $Group.CollectionID -and $_.CollectionID -notin @($Group.CollectionIDsToExclude) -and $_.UserID -eq $ExistingMember.ExternalDirectoryObjectId -and ($_.UserType -eq $Group.UserType -or $IgnoreCollectionIDUserType -eq $true) -and $_.State -gt 1 }).count -gt 0)) { # 1.0.86									
											
											#  write-host "$($ExistingMember.PrimarySMTPAddress) would be removed"
											
											if ( @($UserOutput | where {$_.GroupID -eq $TargetUnifiedGroup.Id -and $_.UserID -eq $ExistingMember.ExternalDirectoryObjectId -and $_.State -gt 1 }).count -eq 0) { # user shouldnt be removed if already added by another settings
											
											
												if ( @($PendingRemovals | where { $_.GroupID -eq $TargetUnifiedGroup.Id -and $_.UserID -eq $ExistingMember.ExternalDirectoryObjectId}).count -eq 0) {	# dont put the user in pending removal for this group if already there
													$PendingRemovalThisGroup +=1
													
													if ($PendingRemovalThisGroup -eq 1) {
														$string = $AddressToUse # 1.0.82
														$string = ", including: $string"
													}
											
													$PendingRemovals += [PSCustomObject]@{
														'UserType' = $Group.UserType
														'Settings' = $Group.Settings
														#'Default' = $Group.Default
														'CollectionID' = $Group.CollectionID
														#'CollectionIDIgnoreThreshold' = $Group.CollectionIDIgnoreThreshold
														'DisplayName' = $ExistingMember.DisplayName
														'EmailAddress' = $AddressToUse
														'Action' = "Removed From Group"
														'GroupName' = $TargetUnifiedGroup.DisplayName
														'UserID' = $ExistingMember.ExternalDirectoryObjectId
														'GroupID' = $TargetUnifiedGroup.Id
														'GroupGuid'  = $TargetUnifiedGroup.Guid.Guid
														'GroupExDirObjectID' = $TargetUnifiedGroup.ExternalDirectoryObjectId
														'GroupDescription' = $TargetUnifiedGroup.Description
														'Simulation' = !$MakeChanges
														'Filter' = $Group.ActionFilter
														'NumberofFilteredUsers' = $OnlineUsers.Count
														'RemoveUnMatchedMembers' = $UnMatchedMembers
														'OnlyRemoveSameType' = $Group.OnlyRemoveSameType
														'SettingsNumber' = $Group.SettingsNumber
														'SettingsDescription' = $Group.Description
														'State' = 1
														'ErrorGenerated' = '-'
														'ErrorMessage' = '-'
													}
												}
											} else {
												$UserOutput += [PSCustomObject]@{
													'UserType' = $Group.UserType
													'Settings' = $Group.Settings
													#'Default' = $Group.Default
													'CollectionID' = $Group.CollectionID
													#'CollectionIDIgnoreThreshold' = $Group.CollectionIDIgnoreThreshold
													'DisplayName' = $ExistingMember.DisplayName
													'EmailAddress' = $AddressToUse
													'Action' = "Not Removed From Group As Already Added or Matched"
													'GroupName' = $TargetUnifiedGroup.DisplayName
													'UserID' = $ExistingMember.ExternalDirectoryObjectId
													'GroupID' = $TargetUnifiedGroup.Id
													'GroupGuid'  = $TargetUnifiedGroup.Guid.Guid
													'GroupExDirObjectID' = $TargetUnifiedGroup.ExternalDirectoryObjectId
													'GroupDescription' = $TargetUnifiedGroup.Description
													'Simulation' = !$MakeChanges
													'Filter' = $Group.ActionFilter
													'NumberofFilteredUsers' = $OnlineUsers.Count
													'RemoveUnMatchedMembers' = $UnMatchedMembers
													'OnlyRemoveSameType' = $Group.OnlyRemoveSameType
													'SettingsNumber' = $Group.SettingsNumber
													'SettingsDescription' = $Group.Description
													'State' = -3
													'ErrorGenerated' = '-'
													'ErrorMessage' = '-'
												}	
											}
										}
									}
									
									if ($ResultCount -eq $LongJobUpdateInterval) {
										[int]$ResultCount=0
										
										$UsersProcessedString, $timestring, $timeremainingseconds = get-timeelapsed -StartTime $funcstarttime -Processed $personprocessed -ToProcess ($ExistingMembers.Count)
										
										if ($timeremainingseconds -ge 10) {

											write-host "$($simstring)Processing Filtered Unified Group Existing Members - CollectionID: $PadCollectionID   $PadSettings   Group ID/Name: $PadGroupName   Processed $UsersProcessedString of $($ExistingMembers.Count) users - ETR$timestring" -ForegroundColor Gray

											if ($DetailedLoggingEnabled) {
												Add-Content -Path "$LogFile" -Value "$($simstring)Processing Filtered Unified Group Existing Members - CollectionID: $PadCollectionID   $PadSettings   Group ID/Name: $PadGroupName   Processed $UsersProcessedString of $($ExistingMembers.Count) users - ETR$timestring" -Encoding Unicode  
											}
										}
									}								
								}
							} else {
								write-host "$($simstring)Processing Filtered Unified Group Existing Members - CollectionID: $PadCollectionID   $PadSettings   Group ID/Name: $PadGroupName   Users to Process: 0 (unmatched users not being removed)" -ForegroundColor $textcolour

								if ($DetailedLoggingEnabled) {
									Add-Content -Path "$LogFile" -Value "$($simstring)Processing Filtered Unified Group Existing Members - CollectionID: $PadCollectionID   $PadSettings   Group ID/Name: $PadGroupName   Users to Process: 0 (unmatched users not being removed)" -Encoding Unicode  
								}							
							}

							# Add people from $UsersToProcess.EmailAddress to the group $TargetUnifiedGroup where they are in the filtered list $OnlineUsers.UserPrincipalName AND NOT already in the $ExistingMember.PrimarySMTPAddress
							[int]$ResultCount=0
							[int]$personprocessed=0
							$funcstarttime = (Get-Date)
							
							foreach ($User in $UsersToProcess) {
								$ResultCount = $ResultCount + 1
								$personprocessed = $personprocessed + 1
								$GroupContainsUser = $false
								if ($ExistingMembers.ExternalDirectoryObjectId -contains $User.ObjectID) { $GroupContainsUser = $true } # 1.0.84
								if ( ( $FilteringEnabled -eq $false -or $OnlineUsers.($ObjectField) -contains $User.ObjectID ) -and $GroupContainsUser -eq $false -and ($Group.HasGroupRules -eq $false -or ($GroupMatchedUsers.ObjectID -contains $User.ObjectID))) { # 1.0.84

									if ($Group.HasADOURules -eq $true) {
										$OU = $null
										$OU = ($AzureUsers | where {$_.ObjectID -eq $User.ObjectID} | Select -First 1).ExtensionProperty.onPremisesDistinguishedName
									}

									if ($Group.HasADOURules -eq $false -or ( (Check_ADCanonicalName -UserCN $OU -RequiredOrOUs $Group.RequiredOrOUs -NotRequiredOUs $Group.NotRequiredOUs) -eq $true)) {
			
										if ($Group.CollectionID -eq 0 -or ( @($UserOutput | where {$_.CollectionID -gt $Group.CollectionIDIgnoreThreshold -and $_.CollectionID -lt $Group.CollectionID -and $_.CollectionID -notin @($Group.CollectionIDsToExclude) -and $_.UserID -eq $User.ObjectID -and ($_.UserType -eq $Group.UserType -or $IgnoreCollectionIDUserType -eq $true) -and $_.State -gt 1 }).count -eq 0)) { #1.0.86
											$UsersAdded +=1

											if ($UsersAdded -eq 1) {
												$stringb = $user.EmailAddress
												$stringb = ", including: $stringb"
											}

											$ErrorGenerated=$false
											$errorstring = ""
											if ($MakeChanges -eq $true) {
												$e = $error.count
												Add-UnifiedGroupLinks -Identity $TargetUnifiedGroup.ExternalDirectoryObjectId -LinkType Members -Links $User.ObjectID -Confirm:$false
												if ($error.count -gt $e) {
													if ($Error[0].Exception.Message) {
														$errorstring = $Error[0].Exception.Message
													} else {
														$errorstring = "(unknown)"
													}
													$ErrorGenerated=$true
													
													if ( ($Group.OnFailureApplytoAllUsersOnNextRun -band 2) -eq 2 -or ($GlobalOnFailureApplytoAllUsersOnNextRun -band 2) -eq 2) {
														if ($Group.UserType -eq 'Teacher') {
															$ApplytoAllTeachersOnNextRunThisFunc+=1
														} else {
															$ApplytoAllStudentsOnNextRunThisFunc+=1		
														}
													}
												}
											}
									
											$PendingRemovals = @($PendingRemovals | where { $_.UserID -ne $User.ObjectID -or $_.GroupExDirObjectID -ne $TargetUnifiedGroup.ExternalDirectoryObjectId} )
											# $PendingRemovals = @($PendingRemovals | where { !($_.UserID -eq $User.ObjectID -and $_.GroupExDirObjectID -eq $TargetUnifiedGroup.ExternalDirectoryObjectId)} ) # alt
									
											$UserOutput += [PSCustomObject]@{
												'UserType' = $Group.UserType
												'Settings' = $Group.Settings
												#'Default' = $Group.Default
												'CollectionID' = $Group.CollectionID
												#'CollectionIDIgnoreThreshold' = $Group.CollectionIDIgnoreThreshold
												'DisplayName' = $user.DisplayName
												'EmailAddress' = $user.EmailAddress
												'Action' = "Added To Group"
												'GroupName' = $TargetUnifiedGroup.DisplayName
												'UserID' = $user.objectID
												'GroupID' = $TargetUnifiedGroup.Id
												'GroupGuid'  = $TargetUnifiedGroup.Guid.Guid
												'GroupExDirObjectID' = $TargetUnifiedGroup.ExternalDirectoryObjectId
												'GroupDescription' = $TargetUnifiedGroup.Description
												'Simulation' = !$MakeChanges
												'Filter' = $Group.ActionFilter
												'NumberofFilteredUsers' = $OnlineUsers.Count
												'RemoveUnMatchedMembers' = $UnMatchedMembers
												'OnlyRemoveSameType' = $Group.OnlyRemoveSameType
												'SettingsNumber' = $Group.SettingsNumber
												'SettingsDescription' = $Group.Description
												'State' = 2
												'ErrorGenerated' = $ErrorGenerated
												'ErrorMessage' = $errorstring
											}
										} else {
											$UserOutput += [PSCustomObject]@{
												'UserType' = $Group.UserType
												'Settings' = $Group.Settings
												#'Default' = $Group.Default
												'CollectionID' = $Group.CollectionID
												#'CollectionIDIgnoreThreshold' = $Group.CollectionIDIgnoreThreshold
												'DisplayName' = $user.DisplayName
												'EmailAddress' = $user.EmailAddress
												'Action' = "User filtered by CollectionID"
												'GroupName' = $TargetUnifiedGroup.DisplayName
												'UserID' = $user.objectID
												'GroupID' = $TargetUnifiedGroup.Id
												'GroupGuid'  = $TargetUnifiedGroup.Guid.Guid
												'GroupExDirObjectID' = $TargetUnifiedGroup.ExternalDirectoryObjectId
												'GroupDescription' = $TargetUnifiedGroup.Description
												'Simulation' = !$MakeChanges
												'Filter' = $Group.ActionFilter
												'NumberofFilteredUsers' = $OnlineUsers.Count
												'RemoveUnMatchedMembers' = $UnMatchedMembers
												'OnlyRemoveSameType' = $Group.OnlyRemoveSameType
												'SettingsNumber' = $Group.SettingsNumber
												'SettingsDescription' = $Group.Description
												'State' = -3
												'ErrorGenerated' = '-'
												'ErrorMessage' = '-'
											}
										}
									} else {

										 $UserOutput += [PSCustomObject]@{
											'UserType' = $Group.UserType
											'Settings' = $Group.Settings
											#'Default' = $Group.Default
											'CollectionID' = $Group.CollectionID
											#'CollectionIDIgnoreThreshold' = $Group.CollectionIDIgnoreThreshold
											'DisplayName' = $user.DisplayName
											'EmailAddress' = $user.EmailAddress
											'Action' = "User Filtered by AD OU/CN Rules"
											'GroupName' = $TargetUnifiedGroup.DisplayName
											'UserID' = $user.objectID
											'GroupID' = $TargetUnifiedGroup.Id
											'GroupGuid'  = $TargetUnifiedGroup.Guid.Guid
											'GroupExDirObjectID' = $TargetUnifiedGroup.ExternalDirectoryObjectId
											'GroupDescription' = $TargetUnifiedGroup.Description
											'Simulation' = !$MakeChanges
											'Filter' = $Group.ActionFilter
											'NumberofFilteredUsers' = $OnlineUsers.Count
											'RemoveUnMatchedMembers' = $UnMatchedMembers
											'OnlyRemoveSameType' = $Group.OnlyRemoveSameType
											'SettingsNumber' = $Group.SettingsNumber
											'SettingsDescription' = $Group.Description
											'State' = 0
											'ErrorGenerated' = '-'
											'ErrorMessage' = '-'
										 }									
									}
								} else {
								
									if ($GroupContainsUser -eq $false) {
										 $UserOutput += [PSCustomObject]@{
											'UserType' = $Group.UserType
											'Settings' = $Group.Settings
											#'Default' = $Group.Default
											'CollectionID' = $Group.CollectionID
											#'CollectionIDIgnoreThreshold' = $Group.CollectionIDIgnoreThreshold
											'DisplayName' = $user.DisplayName
											'EmailAddress' = $user.EmailAddress
											'Action' = "User Filtered by Group Rules or $($Group.FilterSource)"
											'GroupName' = $TargetUnifiedGroup.DisplayName
											'UserID' = $user.objectID
											'GroupID' = $TargetUnifiedGroup.Id
											'GroupGuid'  = $TargetUnifiedGroup.Guid.Guid
											'GroupExDirObjectID' = $TargetUnifiedGroup.ExternalDirectoryObjectId
											'GroupDescription' = $TargetUnifiedGroup.Description
											'Simulation' = !$MakeChanges
											'Filter' = $Group.ActionFilter
											'NumberofFilteredUsers' = $OnlineUsers.Count
											'RemoveUnMatchedMembers' = $UnMatchedMembers
											'OnlyRemoveSameType' = $Group.OnlyRemoveSameType
											'SettingsNumber' = $Group.SettingsNumber
											'SettingsDescription' = $Group.Description
											'State' = 0
											'ErrorGenerated' = '-'
											'ErrorMessage' = '-'
										 }
									} else {
										$UserOutput += [PSCustomObject]@{
											'UserType' = $Group.UserType
											'Settings' = $Group.Settings
											#'Default' = $Group.Default
											'CollectionID' = $Group.CollectionID
											#'CollectionIDIgnoreThreshold' = $Group.CollectionIDIgnoreThreshold
											'DisplayName' = $user.DisplayName
											'EmailAddress' = $user.EmailAddress
											'Action' = "Group contains User"
											'GroupName' = $TargetUnifiedGroup.DisplayName
											'UserID' = $user.objectID
											'GroupID' = $TargetUnifiedGroup.Id
											'GroupGuid'  = $TargetUnifiedGroup.Guid.Guid
											'GroupExDirObjectID' = $TargetUnifiedGroup.ExternalDirectoryObjectId
											'GroupDescription' = $TargetUnifiedGroup.Description
											'Simulation' = !$MakeChanges
											'Filter' = $Group.ActionFilter
											'NumberofFilteredUsers' = $OnlineUsers.Count
											'RemoveUnMatchedMembers' = $UnMatchedMembers
											'OnlyRemoveSameType' = $Group.OnlyRemoveSameType
											'SettingsNumber' = $Group.SettingsNumber
											'SettingsDescription' = $Group.Description
											'State' = 3
											'ErrorGenerated' = '-'
											'ErrorMessage' = '-'
										 }									
									}
								}
								if ($ResultCount -eq $LongJobUpdateInterval) {
									[int]$ResultCount=0
									
									$UsersProcessedString, $timestring, $timeremainingseconds = get-timeelapsed -StartTime $funcstarttime -Processed $personprocessed -ToProcess ($UsersToProcess.Count)
									
									if ($timeremainingseconds -ge 10) {

										write-host "$($simstring)Processing Filtered Unified Group User Additions - CollectionID: $PadCollectionID   $PadSettings   Group ID/Name: $PadGroupName   Processed $UsersProcessedString of $($UsersToProcess.Count) potential users - ETR$timestring" -ForegroundColor Gray

										if ($DetailedLoggingEnabled) {
											Add-Content -Path "$LogFile" -Value "$($simstring)Processing Filtered Unified Group User Additions - CollectionID: $PadCollectionID   $PadSettings   Group ID/Name: $PadGroupName   Processed $UsersProcessedString of $($UsersToProcess.Count) potential users - ETR$timestring" -Encoding Unicode  
										}
									}
								}	
							}

							if ($UsersAdded -gt 0) {
								$Output += [PSCustomObject]@{
									'UserType' = $Group.UserType
									'Settings' = $Group.Settings
									#'Default' = $Group.Default
									'CollectionID' = $Group.CollectionID
									#'CollectionIDIgnoreThreshold' = $Group.CollectionIDIgnoreThreshold
									#'CollectionIDsToExclude' = $Group.CollectionIDsToExclude
									'GroupName' = $TargetUnifiedGroup.DisplayName
									'Simulation' = !$MakeChanges
									'Status' = "Users Added to Group"
									'UsersModified' = $UsersAdded
									#'Description' = $Group.Description
									'Filter' = $Group.ActionFilter
									'GroupID' = $TargetUnifiedGroup.Id
									'GroupGuid'  = $TargetUnifiedGroup.Guid.Guid
									'GroupExDirObjectID' = $TargetUnifiedGroup.ExternalDirectoryObjectId
									'GroupDescription' = $TargetUnifiedGroup.Description
									'SettingsNumber' = $Group.SettingsNumber
									'SettingsDescription' = $Group.Description
								}
							}


			#$onlineusers | where { $_.UserPrincipalName -eq 'upn@school.org.uk' } | fl * << ObjectID
			#$existingMembers | where { $_.PrimarySMTPAddress -eq 'upn@school.org.uk' } | fl * << ExternalDirectoryObjectId 
			#$UsersToProcess | where { $_.EmailAddress -eq 'upn@school.org.uk' } | fl *    << ObjectID

							$PadUsersAdded=[string]$UsersAdded
							$PadUsersAdded = $PadUsersAdded.PadLeft(4," ")

							$PadUsersPendingRemoval=[string]$PendingRemovalThisGroup
							$PadUsersPendingRemoval = $PadUsersPendingRemoval.PadLeft(4," ")

						
							if ($Verbose -eq $true) {   
								write-host "$($SimString)Updated Membership - CollectionID: $PadCollectionID   $PadSettings   Group ID/Name: $PadGroupName             Added Users: $PadUsersAdded$stringb" -ForegroundColor $textcolour
								write-host "$($SimString)Updated Membership - CollectionID: $PadCollectionID   $PadSettings   Group ID/Name: $PadGroupName   Users Pending Removal: $PadUsersPendingRemoval$string" -ForegroundColor $textcolour

							}
							if ($DetailedLoggingEnabled) {
								Add-Content -Path "$LogFile" -Value "$($SimString)Updated Membership - CollectionID: $PadCollectionID   $PadSettings   Group ID/Name: $PadGroupName             Added Users: $PadUsersAdded$stringb" -Encoding Unicode
								Add-Content -Path "$LogFile" -Value "$($SimString)Updated Membership - CollectionID: $PadCollectionID   $PadSettings   Group ID/Name: $PadGroupName   Users Pending Removal: $PadUsersPendingRemoval$string" -Encoding Unicode
							}

						} else {

							$Filter = $null
							$OnlineUsers = @()
							if ($Error[0].Exception.Message) {
								$errorstring = $Error[0].Exception.Message
							} else {
								$errorstring = "(unknown)"
							}
						
							$Output += [PSCustomObject]@{
								'UserType' = $Group.UserType
								'Settings' = $Group.Settings
								#'Default' = $Group.Default
								'CollectionID' = $Group.CollectionID
								#'CollectionIDIgnoreThreshold' = $Group.CollectionIDIgnoreThreshold
								#'CollectionIDsToExclude' = $Group.CollectionIDsToExclude
								'GroupName' = $TargetUnifiedGroup.DisplayName
								'Simulation' = !$MakeChanges
								'Status' = "Unable to get $FilterType $FilterSource Users - error message: " + $errorstring 
								'UsersModified' = 0
								#'Description' = $Group.Description
								'Filter' = $Group.ActionFilter
								'GroupID' = $TargetUnifiedGroup.Id
								'GroupGuid'  = $TargetUnifiedGroup.Guid.Guid
								'GroupExDirObjectID' = $TargetUnifiedGroup.ExternalDirectoryObjectId
								'GroupDescription' = $TargetUnifiedGroup.Description
								'SettingsNumber' = $Group.SettingsNumber
								'SettingsDescription' = $Group.Description
							}
							$PadUsersNotProcessed=[string]$UsersToProcess.count
							$PadUsersNotProcessed = $PadUsersNotProcessed.PadRight(4," ")

							write-host "There was an error in retrieving $FilterType $FilterSource Users - CollectionID: $PadCollectionID   $PadSettings   Group ID/Name: $PadGroupName   Users Not Processed: $PadUsersNotProcessed" -ForegroundColor Red
							$warnings += "- There was an error in retrieving $FilterType $FilterSource Users - CollectionID: $PadCollectionID   $PadSettings   Group ID/Name: $PadGroupName   Users Not Processed: $PadUsersNotProcessed"
							if ($DetailedLoggingEnabled) {
								Add-Content -Path "$LogFile" -Value "There was an error in retrieving $FilterType $FilterSource Users - CollectionID: $PadCollectionID   $PadSettings   Group ID/Name: $PadGroupName   Users Not Processed: $PadUsersNotProcessed" -Encoding Unicode
							}
						}

					} else {
					# group filter means no users can be processed
						$Output += [PSCustomObject]@{
							'UserType' = $Group.UserType
							'Settings' = $Group.Settings
							#'Default' = $Group.Default
							'CollectionID' = $Group.CollectionID
							#'CollectionIDIgnoreThreshold' = $Group.CollectionIDIgnoreThreshold
							#'CollectionIDsToExclude' = $Group.CollectionIDsToExclude
							'GroupName' = $TargetUnifiedGroup.DisplayName
							'Simulation' = !$MakeChanges
							'Status' = "Group Rules Impossible"
							'UsersModified' = 0
							#'Description' = $Group.Description
							'Filter' = $Group.ActionFilter
							'GroupID' = $TargetUnifiedGroup.Id
							'GroupGuid'  = $TargetUnifiedGroup.Guid.Guid
							'GroupExDirObjectID' = $TargetUnifiedGroup.ExternalDirectoryObjectId
							'GroupDescription' = $TargetUnifiedGroup.Description
							'SettingsNumber' = $Group.SettingsNumber
							'SettingsDescription' = $Group.Description
						}
				

						$PadUsersNotProcessed=[string]$UsersToProcess.count
						$PadUsersNotProcessed = $PadUsersNotProcessed.PadRight(4," ")


						write-host "Group Rules Impossible - CollectionID: $PadCollectionID   $PadSettings   Group ID/Name: $PadGroupName   Users Not Processed: $PadUsersNotProcessed" -ForegroundColor Yellow
						$warnings += "- Group Rules Impossible - CollectionID: $PadCollectionID   $PadSettings   Group ID/Name: $PadGroupName   Users Not Processed: $PadUsersNotProcessed"
						if ($DetailedLoggingEnabled) {
							Add-Content -Path "$LogFile" -Value "Group Rules Impossible - CollectionID: $PadCollectionID   $PadSettings   Group ID/Name: $PadGroupName   Users Not Processed: $PadUsersNotProcessed" -Encoding Unicode
						}
					}
					
			    } else {
                # group filter means no users can be processed
                    $Output += [PSCustomObject]@{
						'UserType' = $Group.UserType
						'Settings' = $Group.Settings
						#'Default' = $Group.Default
						'CollectionID' = $Group.CollectionID
						#'CollectionIDIgnoreThreshold' = $Group.CollectionIDIgnoreThreshold
						#'CollectionIDsToExclude' = $Group.CollectionIDsToExclude
						'GroupName' = $TargetUnifiedGroup.DisplayName
						'Simulation' = !$MakeChanges
						'Status' = "Group Rules Impossible or Processing Error"
						'UsersModified' = 0
						#'Description' = $Group.Description
						'Filter' = $Group.ActionFilter
						'GroupID' = $TargetUnifiedGroup.Id
						'GroupGuid'  = $TargetUnifiedGroup.Guid.Guid
						'GroupExDirObjectID' = $TargetUnifiedGroup.ExternalDirectoryObjectId
						'GroupDescription' = $TargetUnifiedGroup.Description
						'SettingsNumber' = $Group.SettingsNumber
						'SettingsDescription' = $Group.Description
                    }

                    $PadUsersNotProcessed=[string]$UsersToProcess.count
                    $PadUsersNotProcessed = $PadUsersNotProcessed.PadRight(4," ")

					write-host "Error retrieving Group Rules Users: CollectionID: $PadCollectionID   $PadSettings   Group ID/Name: $PadGroupName   Users Not Processed: $PadUsersNotProcessed" -ForegroundColor Yellow
					$warnings += "- Error retrieving Group Rules Users: CollectionID: $PadCollectionID   $PadSettings   Group ID/Name: $PadGroupName   Users Not Processed: $PadUsersNotProcessed"
					if ($DetailedLoggingEnabled) {
						Add-Content -Path "$LogFile" -Value "Error retrieving Group Rules Users: CollectionID: $PadCollectionID   $PadSettings   Group ID/Name: $PadGroupName   Users Not Processed: $PadUsersNotProcessed" -Encoding Unicode
					}
                }		
# end 1.0.21
            }

            if ( @($TargetUnifiedGroup).count -eq 0 -or !$TargetUnifiedGroup) {
				if ( ($Group.OnFailureApplytoAllUsersOnNextRun -band 4) -eq 4 -or ($GlobalOnFailureApplytoAllUsersOnNextRun -band 2) -eq 2) { # 1.0.79
					if ($Group.UserType -eq 'Teacher') {
						$ApplytoAllTeachersOnNextRunThisFunc+=$UsersToProcess.count
					} else {
						$ApplytoAllStudentsOnNextRunThisFunc+=$UsersToProcess.count		
					}
				}
                if ($Group.GroupNameAsID -eq $true) {
                    $Output += [PSCustomObject]@{
                        'UserType' = $Group.UserType
						'Settings' = $Group.Settings
						#'Default' = $Group.Default
						'CollectionID' = $Group.CollectionID
						#'CollectionIDIgnoreThreshold' = $Group.CollectionIDIgnoreThreshold
						#'CollectionIDsToExclude' = $Group.CollectionIDsToExclude
                        'GroupName' = ''
                        'Simulation' = !$MakeChanges
                        'Status' = "Group Missing"
                        'UsersModified' = 0
                        #'Description' = $Group.Description
                        'Filter' = $Group.Filter
                        'GroupID' = $Group.GroupName
						'GroupGuid'  = ''
                        'GroupExDirObjectID' = ''
                        'GroupDescription' = ''
						'SettingsNumber' = $Group.SettingsNumber
						'SettingsDescription' = $Group.Description
                    }
                } else {
                    $Output += [PSCustomObject]@{
                        'UserType' = $Group.UserType
						'Settings' = $Group.Settings
						#'Default' = $Group.Default
						'CollectionID' = $Group.CollectionID
						#'CollectionIDIgnoreThreshold' = $Group.CollectionIDIgnoreThreshold
						#'CollectionIDsToExclude' = $Group.CollectionIDsToExclude
                        'GroupName' = $Group.GroupName
                        'Simulation' = !$MakeChanges
                        'Status' = "Group Missing"
                        'UsersModified' = 0
                        #'Description' = $Group.Description
                        'Filter' = $Group.Filter
                        'GroupID' = ''
						'GroupGuid'  = ''
                        'GroupExDirObjectID' = ''
                        'GroupDescription' = ''
						'SettingsNumber' = $Group.SettingsNumber
						'SettingsDescription' = $Group.Description
                    }
                }
            
            
                $PadUsersNotProcessed=[string]$UsersToProcess.count
                $PadUsersNotProcessed = $PadUsersNotProcessed.PadRight(4," ")


                write-host "Unified Group/ID Not Found - CollectionID: $PadCollectionID   $PadSettings   Group ID/Name: $PadGroupName   Users Not Processed: $PadUsersNotProcessed" -ForegroundColor Yellow
                $warnings += "- Unified Group/ID Not Found - CollectionID: $PadCollectionID   $PadSettings   Group ID/Name: $PadGroupName   Users Not Processed: $PadUsersNotProcessed"
                if ($DetailedLoggingEnabled) {
                    Add-Content -Path "$LogFile" -Value "Unified Group/ID Not Found - CollectionID: $PadCollectionID   $PadSettings   Group ID/Name: $PadGroupName   Users Not Processed: $PadUsersNotProcessed" -Encoding Unicode
                }
            }
            if ( @($TargetUnifiedGroup).count -gt 1) {
				if ( ($Group.OnFailureApplytoAllUsersOnNextRun -band 4) -eq 4 -or ($GlobalOnFailureApplytoAllUsersOnNextRun -band 2) -eq 2) { # 1.0.79
					if ($Group.UserType -eq 'Teacher') {
						$ApplytoAllTeachersOnNextRunThisFunc+=$UsersToProcess.count
					} else {
						$ApplytoAllStudentsOnNextRunThisFunc+=$UsersToProcess.count		
					}
				}
                if ($Group.GroupNameAsID -eq $true) {
                    $Output += [PSCustomObject]@{
                        'UserType' = $Group.UserType
						'Settings' = $Group.Settings
						#'Default' = $Group.Default
						'CollectionID' = $Group.CollectionID
						#'CollectionIDIgnoreThreshold' = $Group.CollectionIDIgnoreThreshold
						#'CollectionIDsToExclude' = $Group.CollectionIDsToExclude
                        'GroupName' = ''
                        'Simulation' = !$MakeChanges
                        'Status' = "More than 1 group with that ID"
                        'UsersModified' = 0
                        #'Description' = $Group.Description
                        'Filter' = $Group.Filter
                        'GroupID' = $Group.GroupName
						'GroupGuid'  = ''
                        'GroupExDirObjectID' = ''
                        'GroupDescription' = ''
						'SettingsNumber' = $Group.SettingsNumber
						'SettingsDescription' = $Group.Description
                    }
                } else {
                    $Output += [PSCustomObject]@{
                        'UserType' = $Group.UserType
						'Settings' = $Group.Settings
						#'Default' = $Group.Default
						'CollectionID' = $Group.CollectionID
						#'CollectionIDIgnoreThreshold' = $Group.CollectionIDIgnoreThreshold
						#'CollectionIDsToExclude' = $Group.CollectionIDsToExclude
                        'GroupName' = $Group.GroupName
                        'Simulation' = !$MakeChanges
                        'Status' = "More than 1 group with that name"
                        'UsersModified' = 0
                        #'Description' = $Group.Description
                        'Filter' = $Group.Filter
                        'GroupID' = ''
						'GroupGuid'  = ''
                        'GroupExDirObjectID' = ''
                        'GroupDescription' = ''
						'SettingsNumber' = $Group.SettingsNumber
						'SettingsDescription' = $Group.Description
                    }
                }



                $PadUsersNotProcessed=[string]$UsersToProcess.count
                $PadUsersNotProcessed = $PadUsersNotProcessed.PadRight(4," ")


                write-host "More than 1 Matching Unified Group Found - CollectionID: $PadCollectionID   $PadSettings   Group ID/Name: $PadGroupName   Users Not Processed: $PadUsersNotProcessed" -ForegroundColor Yellow
                $warnings += "- More than 1 Matching Unified Group Found - CollectionID: $PadCollectionID   $PadSettings   Group ID/Name: $PadGroupName   Users Not Processed: $PadUsersNotProcessed"
                if ($DetailedLoggingEnabled) {
                    Add-Content -Path "$LogFile" -Value "More than 1 Matching Unified Group Found - CollectionID: $PadCollectionID   $PadSettings   Group ID/Name: $PadGroupName   Users Not Processed: $PadUsersNotProcessed" -Encoding Unicode
                }

            }
       
        } # end for group loop

		if ( @($PendingRemovals).Count -gt 0) {
			[int]$GroupsProcessed=0
			[int]$ResultCount=0
            $funcstarttime = (Get-Date)
			
			write-host "$($SimString)Processing Accumulated Filtered Unified Group User Removals - Users Pending Removal across all Groups: $($PendingRemovals.Count)" -ForegroundColor $textcolour
			if ($DetailedLoggingEnabled) {
                    Add-Content -Path "$LogFile" -Value "$($SimString)Processing Accumulated Filtered Unified Group User Removals - Users Pending Removal across all Groups: $($PendingRemovals.Count)" -Encoding Unicode
            }
			[int]$TotalUsersRemoved=0
			foreach ($Group in $FilteredUnifiedGroupsToProcess) {
				$GroupsProcessed = $GroupsProcessed +1
				[int]$UsersRemoved=0
				$FilteredPendingRemovals = @($PendingRemovals | where {$_.UserType -eq $Group.UserType -and $_.Settings -eq $Group.Settings -and $_.State -eq 1})
				
				if ($Group.Description) {
					$DescString = "$($Group.Settings) $($Group.Description)"
				} else {
					$DescString = "$($Group.Settings)"
				}

				$ProcessedString = [string]$GroupsProcessed
				$ProcessedString = $ProcessedString.PadLeft(($FilteredUnifiedGroupsToProcess.Count -as [string]).length," ")

				if ( $FilteredPendingRemovals.Count -gt 0) {
					$string = $FilteredPendingRemovals[0].EmailAddress
					$string = ", including: $string"
				} else {
					$string = ''
				}
				$PadGroupName=$Group.GroupName.PadRight($GroupGroupNameMaxLength," ")
				$PadSettings=$Group.Settings.PadRight($SettingsMaxLength," ")
				$PadCollectionID=([string]$Group.CollectionID).PadRight($CollectionIDMaxLength," ")
				
				$PadUsersPendingRemoval=[string]$FilteredPendingRemovals.Count
                $PadUsersPendingRemoval = $PadUsersPendingRemoval.PadLeft(4," ")

				write-host "$($SimString)Processing Group $ProcessedString of $($FilteredUnifiedGroupsToProcess.Count): CollectionID: $PadCollectionID   $PadSettings   Group ID/Name: $PadGroupName   Users Being Removed: $PadUsersPendingRemoval$string" -ForegroundColor $textcolour

				if ($DetailedLoggingEnabled) {
					Add-Content -Path "$LogFile" -Value "`r`n$($SimString)Processing Group $ProcessedString of $($FilteredUnifiedGroupsToProcess.Count): CollectionID: $PadCollectionID   $PadSettings   Group ID/Name: $PadGroupName   Users Being Removed: $PadUsersPendingRemoval$string" -Encoding Unicode
				}

				if ( $FilteredPendingRemovals.Count -gt 0) {
					
					foreach ($PendingUser in $FilteredPendingRemovals) {
						$ResultCount = $ResultCount + 1
						$UsersRemoved+=1

						$ErrorGenerated=$false
						$errorstring = ""
						if ($MakeChanges -eq $true) {
							$e = $error.count
							Remove-UnifiedGroupLinks -Identity $PendingUser.GroupExDirObjectID -LinkType Members -Links $PendingUser.UserID -Confirm:$false
							if ($error.count -gt $e) {
								if ($Error[0].Exception.Message) {
									$errorstring = $Error[0].Exception.Message
								} else {
									$errorstring = "(unknown)"
								}
							$ErrorGenerated=$true
							}
						} 

						$UserOutput += [PSCustomObject]@{
							'UserType' = $PendingUser.UserType
							'Settings' = $PendingUser.Settings
							#'Default' = $Group.Default
							'CollectionID' = $PendingUser.CollectionID
							#'CollectionIDIgnoreThreshold' = $PendingUser.CollectionIDIgnoreThreshold
							'DisplayName' = $PendingUser.DisplayName
							'EmailAddress' = $PendingUser.EmailAddress
							'Action' = "Removed From Group"
							'GroupName' = $PendingUser.GroupName
							'UserID' = $PendingUser.UserID
							'GroupID' = $PendingUser.GroupID
							'GroupGuid'  = $PendingUser.GroupGuid
							'GroupExDirObjectID' = $PendingUser.GroupExDirObjectID
							'GroupDescription' = $PendingUser.GroupDescription
							'Simulation' = !$MakeChanges
							'Filter' = $PendingUser.Filter
							'NumberofFilteredUsers' = $PendingUser.NumberofFilteredUsers
							'RemoveUnMatchedMembers' = $PendingUser.RemoveUnMatchedMembers
							'OnlyRemoveSameType' = $PendingUser.OnlyRemoveSameType
							'SettingsNumber' = $PendingUser.SettingsNumber
							'SettingsDescription' = $PendingUser.SettingsDescription
							'State' = 1
							'ErrorGenerated' = $ErrorGenerated
							'ErrorMessage' = $errorstring
						}
					
						if ($ResultCount -eq $LongJobUpdateInterval) {
							[int]$ResultCount=0
							
							$UsersProcessedString, $timestring, $timeremainingseconds = get-timeelapsed -StartTime $funcstarttime -Processed ($TotalUsersRemoved+$UsersRemoved) -ToProcess ($PendingRemovals.Count)
							
							if ($timeremainingseconds -ge 10) {

								write-host "$($simstring)Processing Accumulated Filtered Unified Group User Removals - Processed $UsersProcessedString of $($PendingRemovals.Count) users - ETR$timestring" -ForegroundColor Gray

								if ($DetailedLoggingEnabled) {
									Add-Content -Path "$LogFile" -Value "$($simstring)Processing Accumulated Filtered Unified Group User Removals - Processed $UsersProcessedString of $($UsersToApply.Count) users - ETR$timestring" -Encoding Unicode  
								}
							}
						}
					}

					$Output += [PSCustomObject]@{
						'UserType' = $Group.UserType
						'Settings' = $Group.Settings
						#'Default' = $Group.Default
						'CollectionID' = $Group.CollectionID
						#'CollectionIDIgnoreThreshold' = $Group.CollectionIDIgnoreThreshold
						#'CollectionIDsToExclude' = $Group.CollectionIDsToExclude
						'GroupName' = $FilteredPendingRemovals[0].GroupName
						'Simulation' = !$MakeChanges
						'Status' = "Users Removed From Group"
						'UsersModified' = $UsersRemoved
						#'Description' = $Group.Description
						'Filter' = $Group.ActionFilter
						'GroupID' = $FilteredPendingRemovals[0].GroupID
						'GroupGuid' = $FilteredPendingRemovals[0].GroupGuid
						'GroupExDirObjectID' = $FilteredPendingRemovals[0].GroupExDirObjectID
						'GroupDescription' = $FilteredPendingRemovals[0].GroupDescription
						'SettingsNumber' = $FilteredPendingRemovals[0].SettingsNumber
						'SettingsDescription' = $FilteredPendingRemovals[0].SettingsDescription
					}
				$TotalUsersRemoved+=$UsersRemoved
				}	
			}
			if ($TotalUsersRemoved -ne @($PendingRemovals).Count) {
				write-host "Filtered Unified Group Processing Error: Total Users Removed: $UsersRemoved - Total Pending Removal: $($PendingRemovals.Count)" -ForegroundColor Red
				$MajorErrors += "- Filtered Unified Group Processing Error: Total Users Removed: $UsersRemoved - Total Pending Removal: $($PendingRemovals.Count)"
				if ($DetailedLoggingEnabled) {
                    Add-Content -Path "$LogFile" -Value "Filtered Unified Group Processing Error: Total Users Removed: $UsersRemoved - Total Pending Removal: $($PendingRemovals.Count)" -Encoding Unicode
                }
			}
		} else {
			write-host "$($SimString)Processing Accumulated Filtered Unified Group User Removals - Users Pending Removal across all Groups: $($PendingRemovals.Count)" -ForegroundColor $textcolour
			if ($DetailedLoggingEnabled) {
                    Add-Content -Path "$LogFile" -Value "$($SimString)Processing Accumulated Filtered Unified Group User Removals - Users Pending Removal across all Groups: $($PendingRemovals.Count)" -Encoding Unicode
            }	
		}
		
		$UserOutput = @($UserOutput | Sort @{Expression = "UserType"; Descending = $true},@{Expression = "CollectionID"; Descending = $false},@{Expression = "SettingsNumber"; Descending = $false},@{Expression = "State"; Descending = $false},@{Expression = "EmailAddress"; Descending = $false})
		$Output = @($Output | Sort @{Expression = "UserType"; Descending = $true},@{Expression = "CollectionID"; Descending = $false},@{Expression = "SettingsNumber"; Descending = $false})
		
        $TeacherGroupsProcessed = @($Output | where { $_.UserType -eq 'Teacher' })
        $TeacherQty = ($TeacherGroupsProcessed.usersmodified | Measure-Object -sum).sum
        $TeacherGroups = @($FilteredUnifiedGroupsToProcess | where {$_.UserType -eq 'Teacher' }).count

        $results += [PSCustomObject]@{
            'Type' = "Filtered Unified Group Processing - $($TeacherGroupsProcessed.Count) Teacher Unified Groups"
		    'Quantity' = $TeacherQty
		    'Source' = "$TeacherGroups Groups"
            'Action' = if ($TeacherQty -gt 0) { "$($SimString)Group Membership Updated" } else { "$($SimString)Group Membership Checked" }
            'Target' = 'Teachers'
            'OtherInfo' = ''
            'AssignmentErrors' = @($UserOutput  | where { $_.UserType -eq 'Teacher' -and $ErrorGenerated -eq $true }).count
             }

        $StudentGroupsProcessed = @($Output | where { $_.UserType -eq 'Student' })
        $StudentQty = ($StudentGroupsProcessed.usersmodified | Measure-Object -sum).sum
        $StudentGroups = @($FilteredUnifiedGroupsToProcess | where {$_.UserType -eq 'Student' }).count

        $results += [PSCustomObject]@{
            'Type' = "Filtered Unified Group Processing - $($StudentGroupsProcessed.Count) Student Unified Groups"
		    'Quantity' = $StudentQty
		    'Source' = "$StudentGroups Groups"
            'Action' = if ($StudentQty -gt 0) { "$($SimString)Group Membership Updated" } else { "$($SimString)Group Membership Checked" }
            'Target' = 'Students'
            'OtherInfo' = ''
            'AssignmentErrors' = @($UserOutput  | where { $_.UserType -eq 'Student' -and $ErrorGenerated -eq $true }).count
             }


        if ($DetailedLoggingEnabled) {
            Add-Content -Path "$LogFile" -Value "`r`nFiltered Unified Group Results:" -Encoding Unicode
            Add-Content -Path "$LogFile" -Value "-------------------------------" -Encoding Unicode
        
            Process-UserCSVOutput ($UserOutput)
            $littleobj = @()
			$littleobj = @($UserOutput | where { $_.Action -eq 'Removed From Group' -or $_.Action -eq 'Added To Group'})
            Add-Content -Path "$LogFile" -Value "$($SimString)$($UserOutput.count) Filtered Unified Group Membership Test operations with $($littleobj.count) Total User Membership Changes" -Encoding Unicode
			$littleobj = @($littleobj  | Sort-Object -Property ObjectID -Unique) # 1.0.94
			Add-Content -Path "$LogFile" -Value "$($littleobj.count) Unique Users that required User Membership Changes`r`n" -Encoding Unicode

			$DefaultUserOutput = @($UserOutput | where { $_.Default -eq $true})
			$littleobj = @($DefaultUserOutput | where { $_.Action -eq 'Removed From Group' -or $_.Action -eq 'Added To Group'})
			Add-Content -Path "$LogFile" -Value "$($SimString)$($DefaultUserOutput.count) Filtered Unified Group Add/Remove operations with $($littleobj.count) Total User Membership Changes under Default Processing" -Encoding Unicode
			$littleobj = @($littleobj  | Sort-Object -Property ObjectID -Unique) # 1.0.94
			Add-Content -Path "$LogFile" -Value "$($littleobj.count) Unique Users that required User Membership Changes under Default Processing`r`n" -Encoding Unicode
        
            Add-Content -Path "$LogFile" -Value "Group Results:" -Encoding Unicode
            Add-Content -Path "$LogFile" -Value "--------------" -Encoding Unicode
            $Output | ft * -AutoSize | Out-String -Width 4096 | Out-File -filepath "$LogFile" -append -Encoding Unicode
            Add-Content -Path "$LogFile" -Value "$($SimString)$($Output.Count) Filtered Unified Group Add/Remove Batch Requests Processed with $TeacherQty Teacher Group Membership Changes and $StudentQty Student Group Membership Changes`r`n" -Encoding Unicode   


        }
        if ($MakeChanges -eq $true ) {
            $Actions += "- $($Output.Count) Filtered Unified Group Add/Remove Batch Requests Processed with $TeacherQty Teacher Group Membership Changes and $StudentQty Student Group Membership Changes"
            write-host "$($Output.Count) Filtered Unified Group Add/Remove Batch Requests Processed with $TeacherQty Teacher Group Membership Changes and $StudentQty Student Group Membership Changes" -ForegroundColor Yellow

            $TotalErrors = ($UserOutput | where { $_.ErrorGenerated -eq $true}).Count

            if ( $TotalErrors -gt 0) {
                if ($DetailedLoggingEnabled) {
			        Add-Content -Path "$LogFile" -Value "`r`nAdd/Remove Errors:" -Encoding Unicode
			        Add-Content -Path "$LogFile" -Value "------------------`r`n" -Encoding Unicode
    			    Add-Content -Path "$LogFile" -Value "$TotalErrors Group Add/Remove Errors were encountered during Filtered Unified Group processing" -Encoding Unicode
                }
			    write-host "$TotalErrors Group Add/Remove Errors were encountered during Filtered Unified Group processing" -ForegroundColor Red
			    $warnings += "- $TotalErrors Group Add/Remove Errors were encountered during Filtered Unified Group processing"
		    }

        } else {
            $Actions += "- Simulation: $($Output.Count) Filtered Unified Group Add/Remove Batch Requests Processed with $TeacherQty Teacher Group Membership Changes and $StudentQty Student Group Membership Changes"
            write-host "Simulation: $($Output.Count) Filtered Unified Group Add/Remove Batch Requests Processed with $TeacherQty Teacher Group Membership Changes and $StudentQty Student Group Membership Changes" -ForegroundColor cyan
        }


		if ($ApplytoAllTeachersOnNextRunThisFunc -gt 0 -or $ApplytoAllStudentsOnNextRunThisFunc -gt 0) {
			$ApplytoAllonNextRunSummary += [PSCustomObject]@{
				'Function' = 'Filtered Unified Group Processing'
				'Teachers' = $ApplytoAllTeachersOnNextRunThisFunc 
				'Students' = $ApplytoAllStudentsOnNextRunThisFunc
			}
		}
		if ($ApplytoAllTeachersOnNextRunThisFunc -gt 0) { $ApplytoAllTeachersOnNextRun=$true }
		if ($ApplytoAllStudentsOnNextRunThisFunc -gt 0) { $ApplytoAllStudentsOnNextRun=$true }


    } else {
        write-host "There are no Filtered Unified Groups to Apply" -foregroundcolor white
        $Actions += "- There are no Filtered Unified Groups to Apply"
        if ($DetailedLoggingEnabled) {
            Add-Content -Path "$LogFile" -Value "`r`nApplying Filtered Unified Groups" -Encoding Unicode
            Add-Content -Path "$LogFile" -Value "================================`r`n" -Encoding Unicode
            Add-Content -Path "$LogFile" -Value "Group Changes to Process:" -Encoding Unicode
            Add-Content -Path "$LogFile" -Value "-------------------------" -Encoding Unicode
            Add-Content -Path "$LogFile" -Value "`r`nThere are no Filtered Unified Groups to Apply" -Encoding Unicode
        }
    }
} else {
	if ($DetailedLoggingEnabled) {
		Add-Content -Path "$LogFile" -Value "`r`nFiltered Unified Groups" -Encoding Unicode
		Add-Content -Path "$LogFile" -Value "=======================`r`n" -Encoding Unicode
        Add-Content -Path "$LogFile" -Value "Group Changes that would be Processed:" -Encoding Unicode
		Add-Content -Path "$LogFile" -Value "--------------------------------------" -Encoding Unicode
		if ($FilteredUnifiedGroupsToProcess.Count -gt 0) {
			$FilteredUnifiedGroupsToProcess | select -Property * -ExcludeProperty CollectionIDsToExclude | ft * -AutoSize | Out-String -Width 4096 | Out-File -filepath "$LogFile" -append -Encoding Unicode # 1.0.94
			Add-Content -Path "$LogFile" -Value "Note: If the global filter type setting is set to Remote, the filter source is forced to CSOnline`r`n" -Encoding Unicode
		} else {
			Add-Content -Path "$LogFile" -Value "`r`nThere are no Filtered Unified Groups to Apply" -Encoding Unicode
		}
	}	
}

#exit


# process filtered Distribution Groups
if ($MajorErrors.Count -eq 0) {
	[int]$ApplytoAllTeachersOnNextRunThisFunc=0
	[int]$ApplytoAllStudentsOnNextRunThisFunc=0
    if ($FilteredDistributionGroupsToProcess.Count -gt 0) {
        write-host "Applying Filtered Distribution Groups" -foregroundcolor green        
        if ($DetailedLoggingEnabled) {
            Add-Content -Path "$LogFile" -Value "`r`nApplying Distribution Groups" -Encoding Unicode
            Add-Content -Path "$LogFile" -Value "============================`r`n" -Encoding Unicode
            Add-Content -Path "$LogFile" -Value "Group Changes to Process:" -Encoding Unicode
            Add-Content -Path "$LogFile" -Value "-------------------------" -Encoding Unicode
            $FilteredDistributionGroupsToProcess | select -Property * -ExcludeProperty CollectionIDsToExclude | ft * -AutoSize | Out-String -Width 4096 | Out-File -filepath "$LogFile" -append -Encoding Unicode # 1.0.94
			Add-Content -Path "$LogFile" -Value "Note: If the global filter type setting is set to Remote, the filter source is forced to CSOnline`r`n" -Encoding Unicode
        }
        [int]$GroupsProcessed=0
        $Filter=$null
		$FilterSource=$null
        $OnlineUsers= @()
		$UnfilteredOnlineUsers = @()
        $UserOutput = @()
		$PendingRemovals = @()
		$FilteredPendingRemovals = @()
        $Output = @()

        [int]$GroupGroupNameMaxLength=($FilteredDistributionGroupsToProcess | Select GroupName | Sort -Property { $_.GroupName.length} | select -Last 1).GroupName.Length
		[int]$SettingsMaxLength=($FilteredDistributionGroupsToProcess | Select Settings | Sort -Property { $_.Settings.length} | select -Last 1).Settings.Length
		[int]$CollectionIDMaxLength=(($FilteredDistributionGroupsToProcess | Select CollectionID | Sort -Property { $_.CollectionID} | select -Last 1).CollectionID -as [string]).length
        if ( @($FilteredDistributionGroupsToProcess | where { $_.RemoveUnMatchedMembers -gt 0 }).count -gt 0) {
            $HasRemoveUnMatchedMembers = $true
        } else {
            $HasRemoveUnMatchedMembers = $false
        }

        if ( @($FilteredDistributionGroupsToProcess | where { $_.RemoveUnMatchedMembers -gt 0 -and $_.OnlyRemoveSameType -eq $false }).count -gt 0) {
            $OnlyRemoveSameTypemaxLength = 5
        } else {
            $OnlyRemoveSameTypemaxLength = 4
        }
		
		$AzureUsers = $null

        foreach ($Group in $FilteredDistributionGroupsToProcess) {
            [int]$UsersAdded=0
            
			[int]$PendingRemovalThisGroup=0
			
            [string]$string = ''
			[string]$stringb = ''

            $GroupsProcessed = $GroupsProcessed +1

            if ($Group.Description) {
                $DescString = "$($Group.Description)"
            } else {
                $DescString = "-"
            }
			
            $ProcessedString = [string]$GroupsProcessed
			$ProcessedString = $ProcessedString.PadLeft(($FilteredDistributionGroupsToProcess.Count -as [string]).length," ")

            $PadGroupName=$Group.GroupName.PadRight($GroupGroupNameMaxLength," ")
			$PadSettings=$Group.Settings.PadRight($SettingsMaxLength," ")
			$PadCollectionID=([string]$Group.CollectionID).PadRight($CollectionIDMaxLength," ")
            #$PadUnMatchedMembers = [string]$($Group.RemoveUnMatchedMembers)
            #$PadUnMatchedMembers = $PadUnMatchedMembers.PadRight($RemoveUnMatchedMembersmaxLength," ")

            if ($Group.RemoveUnMatchedMembers -gt 0) {
				$UnMatchedMembers = ""
				if (($Group.RemoveUnMatchedMembers -band 2) -eq 2)  { $UnMatchedMembers = "A" }
				if (($Group.RemoveUnMatchedMembers -band 4) -eq 4)  { $UnMatchedMembers = "$($UnMatchedMembers)G" }
				if (($Group.RemoveUnMatchedMembers -band 8) -eq 8)  { $UnMatchedMembers = "$($UnMatchedMembers)O" }
				if (($Group.RemoveUnMatchedMembers -band 16) -eq 16)  { $UnMatchedMembers = "$($UnMatchedMembers)C" }
				$PadUnMatchedMembers = $UnMatchedMembers.PadRight(4," ")
                $PadOnlyRemoveSameType = [string]$($Group.OnlyRemoveSameType)
                $PadOnlyRemoveSameType = $PadOnlyRemoveSameType.PadRight($OnlyRemoveSameTypemaxLength," ")
                $PadOnlyRemoveSameType = "- $($Group.UserType)s Only: $PadOnlyRemoveSameType "
            } else {
                $PadUnMatchedMembers = "    "
				$UnMatchedMembers = $false
				$PadOnlyRemoveSameType=""
                if ($HasRemoveUnMatchedMembers -eq $true) {
                    $PadOnlyRemoveSameType = $PadOnlyRemoveSameType.PadRight($OnlyRemoveSameTypemaxLength," ")
                    $PadOnlyRemoveSameType = "                $PadOnlyRemoveSameType "
                }
            }
            
            write-host "$($SimString)Processing Group $ProcessedString of $($FilteredDistributionGroupsToProcess.Count): CollectionID: $PadCollectionID   $PadSettings   Group ID/Name: $PadGroupName   Remove UnMatched Members: $PadUnMatchedMembers $PadOnlyRemoveSameType   Desc: $DescString" -ForegroundColor $textcolour
            
            if ($DetailedLoggingEnabled) {
                Add-Content -Path "$LogFile" -Value "`r`n$($SimString)Processing Group $ProcessedString of $($FilteredDistributionGroupsToProcess.Count): CollectionID: $PadCollectionID   $PadSettings   Group ID/Name: $PadGroupName   Remove UnMatched Members: $PadUnMatchedMembers $PadOnlyRemoveSameType   Desc: $DescString" -Encoding Unicode
            }

			$string=$null
			$string=""
            if ($Group.UserType -eq 'Student') {
                if ($Group.ApplyToAll -eq $true -or $ApplytoAllStudents -eq $true) {
                    $UsersToProcess = $StudentList
                } else {
                    $UsersToProcess = $StudentsNotInLicensingGroup
					$string=" that were not members of the Licensing Group"
                }
            }
            if ($Group.UserType -eq 'Teacher') {
                if ($Group.ApplyToAll -eq $true -or $ApplytoAllTeachers -eq $true) {
                    $UsersToProcess = $TeacherList
                } else {
                    $UsersToProcess = $TeachersNotInLicensingGroup
					$string=" that were not members of the Licensing Group"
                }
            }

			if ($Group.DomainFilter ) {
				$UsersToProcess = @($UsersToProcess | where { $Group.DomainFilter -contains $_.EmailAddress.Split('@')[1] })
				$string="$string and were filtered by the Domain Filter"
			}

            if ($Group.OnlyApplyToUnlicensed -eq $true) {
                $UsersToProcess = @($UsersToProcess | where { $_.IsLicensed -eq $false })
				write-host "$($SimString)Using $($UsersToProcess.count) Unlicensed Source List $($Group.UserType)s$String" -ForegroundColor $textcolour	
				if ($DetailedLoggingEnabled) {
					Add-Content -Path "$LogFile" -Value "$($SimString)Using $($UsersToProcess.count) Unlicensed Source List $($Group.UserType)s$String"
				}				
            } else {
				write-host "$($SimString)Using $($UsersToProcess.count) Source List $($Group.UserType)s$String" -ForegroundColor $textcolour	
				if ($DetailedLoggingEnabled) {
					Add-Content -Path "$LogFile" -Value "$($SimString)Using $($UsersToProcess.count) Source List $($Group.UserType)s$String"
				}	
			}
			$string=$null

            $RequiredRecursiveIDs = @()
            $NotRequiredRecursiveIDs = @()
            $ProblemGroups = @()
            $GroupRulesOK = $true
			$GotGroupUsersOK = $true
			$GroupMatchedUsers = @()
            if ($Group.HasGroupRules -eq $true ) {

                write-host "$($SimString)Building Group Rules Table" -ForegroundColor $textcolour
                
                if ($DetailedLoggingEnabled) {
                    Add-Content -Path "$LogFile" -Value "`r`n$($SimString)Building Group Rules Table`r`n" -Encoding Unicode
                }
				$e = $error.count
                $GroupRulesOK, $ProblemGroups, $RequiredRecursiveIDs, $RequiredORRecursiveIDs, $NotRequiredRecursiveIDs = BuildGroupTable -GroupInformation $Group

                if ($GroupRulesOK -eq $true) {
                    write-host "$($SimString)Group Rules OK: $GroupRulesOK   Problem Groups: $($ProblemGroups.count)   RequiredGroups: $($RequiredRecursiveIDs.count)   RequiredOrGroups: $($RequiredORRecursiveIDs.count)   NotRequiredGroups: $($NotRequiredRecursiveIDs.count)" -ForegroundColor $textcolour
					$GroupMatchedUsers = GetGroupRuleUsers -RequiredRecursiveIDs $RequiredRecursiveIDs -RequiredORRecursiveIDs $RequiredORRecursiveIDs -NotRequiredRecursiveIDs $NotRequiredRecursiveIDs
					write-host "$($SimString)Found $($GroupMatchedUsers.count) users matching the Group Rules" -ForegroundColor $textcolour
                } else {
                    write-host "$($SimString)Group Rules OK: $GroupRulesOK   Problem Groups: $($ProblemGroups.count)   RequiredGroups: $($RequiredRecursiveIDs.count)   RequiredOrGroups: $($RequiredORRecursiveIDs.count)   NotRequiredGroups: $($NotRequiredRecursiveIDs.count)" -ForegroundColor yellow
                    $warnings += "- $($SimString)Filtered Distribution Group $($GroupsProcessed): Group Rules OK: $GroupRulesOK   Problem Groups: $($ProblemGroups.count)   RequiredGroups: $($RequiredRecursiveIDs.count)   RequiredOrGroups: $($RequiredORRecursiveIDs.count)   NotRequiredGroups: $($NotRequiredRecursiveIDs.count)"

                }
				
                if ($error.count -gt $e) {
                    $errorstring = ""
			        if ($Error[0].Exception.Message) {
				        $errorstring = $Error[0].Exception.Message
			        } else {
				        $errorstring = "(unknown)"
			        }
					write-host "There was an error in processing Group Rules or matching users - error message: $errorstring" -foreground red
					if ($DetailedLoggingEnabled) {
						Add-Content -Path "$LogFile" -Value "There was an error in processing Group Rules or matching users - error message: $errorstring" -Encoding Unicode
					}
					$Warnings += "- $($SimString)Filtered Distribution Group $($GroupsProcessed): There was an error in processing Group Rules or matching users - error message: $errorstring"
					$GotGroupUsersOK = $false
                }

                if ($DetailedLoggingEnabled) {
                    if ($RequiredRecursiveIDs.count -gt 0) {
						Add-Content -Path "$LogFile" -Value "Group Rules: RequiredGroups, Including Recursive"
                        Add-Content -Path "$LogFile" -Value "------------------------------------------------"
                        $RequiredRecursiveIDs | Sort DisplayName,  CommonName, Description, ObjectID | fl * | Out-String -Width 4096 | Out-File -filepath "$LogFile" -append -Encoding Unicode
                    }
                    if ($RequiredORRecursiveIDs.count -gt 0) {
						Add-Content -Path "$LogFile" -Value "Group Rules: RequiredOrGroups, Including Recursive"
                        Add-Content -Path "$LogFile" -Value "--------------------------------------------------"
                        $RequiredORRecursiveIDs | Sort DisplayName,  CommonName, Description, ObjectID | fl * | Out-String -Width 4096 | Out-File -filepath "$LogFile" -append -Encoding Unicode
                    }					
                    if ($NotRequiredRecursiveIDs.count -gt 0) {
						Add-Content -Path "$LogFile" -Value "Group Rules: NotRequiredGroups, Including Recursive"
                        Add-Content -Path "$LogFile" -Value "---------------------------------------------------"
                        $NotRequiredRecursiveIDs | Sort DisplayName,  CommonName, Description, ObjectID | fl * | Out-String -Width 4096 | Out-File -filepath "$LogFile" -append -Encoding Unicode
                    }
                    if ($ProblemGroups.count -gt 0) {
                        Add-Content -Path "$LogFile" -Value "Group Rules: Problem Groups"
                        Add-Content -Path "$LogFile" -Value "---------------------------"
                        $ProblemGroups | Sort DisplayName,  CommonName, Description, ObjectID | fl * | Out-String -Width 4096 | Out-File -filepath "$LogFile" -append -Encoding Unicode
                    }
                    Add-Content -Path "$LogFile" -Value "$($SimString)Group Rules OK: $GroupRulesOK   Problem Groups: $($ProblemGroups.count)   RequiredGroups: $($RequiredRecursiveIDs.count)   RequiredOrGroups: $($RequiredORRecursiveIDs.count)   NotRequiredGroups: $($NotRequiredRecursiveIDs.count)" -Encoding Unicode
					Add-Content -Path "$LogFile" -Value "$($SimString)Found $($GroupMatchedUsers.count) users matching the Group Rules" -Encoding Unicode
                }
            }
# end 1.0.21

			if ($Group.HasADOURules -eq $true ) {
					write-host "$($SimString)Active Directory OU/CN Rules - Included OU/CN Or Rules: $($Group.RequiredOrOUs.count)   Not Required OU/CN Rules: $($Group.NotRequiredOUs.count)" -ForegroundColor $textcolour	
				
				if ($DetailedLoggingEnabled) {
					Add-Content -Path "$LogFile" -Value "`r`nActive Directory OU/CN Rules"
                    Add-Content -Path "$LogFile" -Value "----------------------------"
					$Group | select RequiredOrOUs, NotRequiredOUs | Sort RequiredOrOUs, NotRequiredOUs | ft * -AutoSize | Out-String -Width 4096 | Out-File -filepath "$LogFile" -append -Encoding Unicode
	
					Add-Content -Path "$LogFile" -Value "$($SimString)Included OU/CN Or Rules: $($Group.RequiredOrOUs.count)   Not Required OU/CN Rules: $($Group.NotRequiredOUs.count)"
				}
			}

            $TargetDistributionGroup = $null
            if ($Group.GroupNameAsID -eq $true) {
                $TargetDistributionGroup = @(get-DistributionGroup -Identity $Group.GroupName -ErrorAction SilentlyContinue -WarningAction SilentlyContinue)
                if ($Group.DisableNameOrIDChecking -eq $false) {
                    $TargetDistributionGroup = $TargetDistributionGroup | where { $_.ExchangeObjectId -like $Group.GroupName -or $_.ExternalDirectoryObjectID -like $Group.GroupName -or $_.GUID -like $Group.GroupName } # 1.0.7
                }
            } else {
                $TargetDistributionGroup = @(get-DistributionGroup -Identity $Group.GroupName -ErrorAction SilentlyContinue -WarningAction SilentlyContinue)
                if ($Group.DisableNameOrIDChecking -eq $false) {
                    $TargetDistributionGroup = $TargetDistributionGroup | where { $_.Name -like $Group.GroupName -or $_.DisplayName -like $Group.GroupName -or $_.Alias -like $Group.GroupName -or $_.PrimarySMTPAddress -like $Group.GroupName -or $_.SamAccountName -like $Group.GroupName  } # 1.0.7
                }
            }  # code is actually the same for both

			
			#if ( @($TargetDistributionGroup).count -eq 1) {
            if ( @($TargetDistributionGroup).count -eq 1) { # 1.0.6
				if ($GotGroupUsersOK -eq $true) {
					if ($GroupRulesOK -eq $true) {
						$e = $error.count

						if ($Group.HasADOURules -eq $true -and !$AzureUsers) {
							$AzureUsers = @(AzureADPreview\Get-AzureADUser -all $true -ErrorAction SilentlyContinue -WarningAction SilentlyContinue)
						}

						# Get All Filtered Online Users
						if ($Group.ActionFilter) {
							$FilteringEnabled = $true
							if ($Filter -ne $Group.ActionFilter -or $FilterSource -ne $Group.FilterSource) {
								$Filter = $Group.ActionFilter
								$FilterSource = $Group.FilterSource
								
								if ($UseLocalAttributeFiltering -eq $false) {
									$OnlineUsers = @(Get-CSOnlineUser -Filter $Filter -ErrorAction SilentlyContinue -WarningAction SilentlyContinue)
									$ObjectField = 'Identity'
								} else {
									if ($FilterSource -eq 'AzureAD') {
										if (!$UnfilteredOnlineUsers) {
											if ($AzureUsers) {
												$UnfilteredOnlineUsers = @($AzureUsers)
											} else {
												$UnfilteredOnlineUsers = @(AzureADPreview\Get-AzureADUser -all $true -ErrorAction SilentlyContinue -WarningAction SilentlyContinue)
											}
										}
										$OnlineUsers = @($UnfilteredOnlineUsers | where { Invoke-Expression $Filter })	
										$ObjectField = 'ObjectID'
									} else {
										if (!$UnfilteredOnlineUsers) {
											$UnfilteredOnlineUsers = @(Get-CSOnlineUser -ErrorAction SilentlyContinue -WarningAction SilentlyContinue)
										}
										$OnlineUsers = @($UnfilteredOnlineUsers | where { Invoke-Expression $Filter })
										$ObjectField = 'Identity'
									}
								}
							}
						} else {
							$FilteringEnabled = $false
							#if ($Filter -or $OnlineUsers.count -eq 0) {
								#$Filter = $null
								#$FilterSource = $Group.FilterSource
								#if ($UseLocalAttributeFiltering -eq $false) {
									#$OnlineUsers = @(Get-CSOnlineUser -ErrorAction SilentlyContinue -WarningAction SilentlyContinue)
								#} else {
									#if ($FilterSource -eq 'AzureAD') {
										#if (!$UnfilteredOnlineUsers) {
											#$UnfilteredOnlineUsers = @(AzureADPreview\Get-AzureADUser -all $true -ErrorAction SilentlyContinue -WarningAction SilentlyContinue)
										#}
										#$OnlineUsers = @($UnfilteredOnlineUsers)
									#} else {
										#if (!$UnfilteredOnlineUsers) {
											#$UnfilteredOnlineUsers = @(Get-CSOnlineUser -ErrorAction SilentlyContinue -WarningAction SilentlyContinue)
										#}
										#$OnlineUsers = @($UnfilteredOnlineUsers)
									#}
								#}
							#}
						}

						if ($error.count -le $e) {

							# Get Existing Group Members
							$ExistingMembers = $null
							$ExistingMembers = @(get-distributiongroupmember -identity $TargetDistributionGroup.ExternalDirectoryObjectId -Resultsize Unlimited -ErrorAction SilentlyContinue -WarningAction SilentlyContinue)
							$ExistingMembers = @( $ExistingMembers | Sort PrimarySMTPAddress ) # 1.0.61

							if ($Group.RemoveUnMatchedMembers -gt 0) {
								if ($Group.UserType -eq 'Teacher') {
									$BaseUserGroup = $Teacherlist
								} else {
									$BaseUserGroup = $Studentlist
								}

								[int]$ResultCount=0
								[int]$personprocessed=0
								$funcstarttime = (Get-Date)
								
								write-host "$($simstring)Processing Filtered Distribution Group Existing Members - CollectionID: $PadCollectionID   $PadSettings   Group ID/Name: $PadGroupName   Users to Process: $($ExistingMembers.Count)" -ForegroundColor $textcolour

								if ($DetailedLoggingEnabled) {
									Add-Content -Path "$LogFile" -Value "$($simstring)Processing Filtered Distribution Group Existing Members - CollectionID: $PadCollectionID   $PadSettings   Group ID/Name: $PadGroupName   Users to Process: $($ExistingMembers.Count)" -Encoding Unicode  
								}
								
								foreach ($ExistingMember in $ExistingMembers) { # compare existing members against csonlineusers (filtered), but make sure that user type is matched
									$ResultCount = $ResultCount + 1
									$personprocessed = $personprocessed + 1
									#if ($Group.OnlyRemoveSameType -eq $false -or $BaseUserGroup.EmailAddress -contains $ExistingMember.PrimarySmtpAddress) {
									if ($Group.OnlyRemoveSameType -eq $false -or $BaseUserGroup.ObjectID -contains $ExistingMember.ExternalDirectoryObjectId) { # 1.0.85

										$OU = $null
										$OU = ($AzureUsers | where {$_.ObjectID -eq $ExistingMember.ExternalDirectoryObjectId} | Select -First 1).ExtensionProperty.onPremisesDistinguishedName

										if ( ($FilteringEnabled -eq $true -and (($Group.RemoveUnMatchedMembers -band 2) -eq 2) -and $OnlineUsers.($ObjectField) -notcontains $ExistingMember.ExternalDirectoryObjectId) -or ($Group.HasGroupRules -eq $true -and (($Group.RemoveUnMatchedMembers -band 4) -eq 4) -and ($GroupMatchedUsers.ObjectID -notcontains $ExistingMember.ExternalDirectoryObjectId)) -or ($Group.HasADOURules -eq $true -and (($Group.RemoveUnMatchedMembers -band 8) -eq 8) -and $OU -and (Check_ADCanonicalName -UserCN $OU -RequiredOrOUs $Group.RequiredOrOUs -NotRequiredOUs $Group.NotRequiredOUs) -ne $true) -or  ( (($Group.RemoveUnMatchedMembers -band 16) -eq 16) -and @($UserOutput | where {$_.CollectionID -gt $Group.CollectionIDIgnoreThreshold -and $_.CollectionID -lt $Group.CollectionID -and $_.CollectionID -notin @($Group.CollectionIDsToExclude) -and $_.UserID -eq $ExistingMember.ExternalDirectoryObjectId -and ($_.UserType -eq $Group.UserType -or $IgnoreCollectionIDUserType -eq $true) -and $_.State -gt 1 }).count -gt 0)) { # 1.0.86

										  #  write-host "$($ExistingMember.PrimarySmtpAddress) would be removed"

											if ( @($UserOutput | where {$_.GroupExDirObjectID -eq $TargetDistributionGroup.ExternalDirectoryObjectId -and $_.UserID -eq $ExistingMember.ExternalDirectoryObjectId -and $_.State -gt 1 }).count -eq 0) { # user shouldnt be removed if already added by another settings

												if ( @($PendingRemovals | where { $_.GroupExDirObjectID -eq $TargetDistributionGroup.ExternalDirectoryObjectId -and $_.UserID -eq $ExistingMember.ExternalDirectoryObjectId}).count -eq 0) {	# dont put the user in pending removal for this group if already there
													$PendingRemovalThisGroup +=1
													
													if ($PendingRemovalThisGroup -eq 1) {
														$string = $ExistingMember.PrimarySMTPAddress
														$string = ", including: $string"
													}
													$PendingRemovals += [PSCustomObject]@{
														'UserType' = $Group.UserType
														'Settings' = $Group.Settings
														#'Default' = $Group.Default
														'CollectionID' = $Group.CollectionID
														#'CollectionIDIgnoreThreshold' = $Group.CollectionIDIgnoreThreshold
														'DisplayName' = $ExistingMember.DisplayName
														'EmailAddress' = $ExistingMember.PrimarySmtpAddress
														'Action' = "Removed From Group"
														'GroupName' = $TargetDistributionGroup.DisplayName
														'UserID' = $ExistingMember.ExternalDirectoryObjectId
														'GroupID' = $TargetDistributionGroup.Id
														'GroupGuid' = $TargetDistributionGroup.Guid.Guid
														'GroupExDirObjectID' = $TargetDistributionGroup.ExternalDirectoryObjectId
														'GroupType' = $TargetDistributionGroup.GroupType
														'GroupEmail' = $TargetDistributionGroup.PrimarySMTPAddress
														'Simulation' = !$MakeChanges
														'Filter' = $Group.ActionFilter
														'NumberofFilteredUsers' = $OnlineUsers.Count
														'RemoveUnMatchedMembers' = $UnMatchedMembers
														'OnlyRemoveSameType' = $Group.OnlyRemoveSameType
														'SettingsNumber' = $Group.SettingsNumber
														'SettingsDescription' = $Group.Description
														'State' = 1
														'ErrorGenerated' = '-'
														'ErrorMessage' = '-'
													}										
												}
											} else {
												$UserOutput += [PSCustomObject]@{
													'UserType' = $Group.UserType
													'Settings' = $Group.Settings
													#'Default' = $Group.Default
													'CollectionID' = $Group.CollectionID
													#'CollectionIDIgnoreThreshold' = $Group.CollectionIDIgnoreThreshold
													'DisplayName' = $ExistingMember.DisplayName
													'EmailAddress' = $ExistingMember.PrimarySmtpAddress
													'Action' = "Not Removed From Group As Already Added or Matched"
													'GroupName' = $TargetDistributionGroup.DisplayName
													'UserID' = $ExistingMember.ExternalDirectoryObjectId
													'GroupID' = $TargetDistributionGroup.Id
													'GroupGuid' = $TargetDistributionGroup.Guid.Guid
													'GroupExDirObjectID' = $TargetDistributionGroup.ExternalDirectoryObjectId
													'GroupType' = $TargetDistributionGroup.GroupType
													'GroupEmail' = $TargetDistributionGroup.PrimarySMTPAddress
													'Simulation' = !$MakeChanges
													'Filter' = $Group.ActionFilter
													'NumberofFilteredUsers' = $OnlineUsers.Count
													'RemoveUnMatchedMembers' = $UnMatchedMembers
													'OnlyRemoveSameType' = $Group.OnlyRemoveSameType
													'SettingsNumber' = $Group.SettingsNumber
													'SettingsDescription' = $Group.Description
													'State' = -3
													'ErrorGenerated' = '-'
													'ErrorMessage' = '-'
												}	
											}										
										}
									}
									
									if ($ResultCount -eq $LongJobUpdateInterval) {
										[int]$ResultCount=0
										
										$UsersProcessedString, $timestring, $timeremainingseconds = get-timeelapsed -StartTime $funcstarttime -Processed $personprocessed -ToProcess ($ExistingMembers.Count)
										
										if ($timeremainingseconds -ge 10) {

											write-host "$($simstring)Processing Filtered Distribution Group Existing Members - CollectionID: $PadCollectionID   $PadSettings   Group ID/Name: $PadGroupName   Processed $UsersProcessedString of $($ExistingMembers.Count) users - ETR$timestring" -ForegroundColor Gray

											if ($DetailedLoggingEnabled) {
												Add-Content -Path "$LogFile" -Value "$($simstring)Processing Filtered Distribution Group Existing Members - CollectionID: $PadCollectionID   $PadSettings   Group ID/Name: $PadGroupName   Processed $UsersProcessedString of $($ExistingMembers.Count) users - ETR$timestring" -Encoding Unicode  
											}
										}
									}								
								}
							} else {
								write-host "$($simstring)Processing Filtered Distribution Group Existing Members - CollectionID: $PadCollectionID   $PadSettings   Group ID/Name: $PadGroupName   Users to Process: 0 (unmatched users not being removed)" -ForegroundColor $textcolour

								if ($DetailedLoggingEnabled) {
									Add-Content -Path "$LogFile" -Value "$($simstring)Processing Filtered Distribution Group Existing Members - CollectionID: $PadCollectionID   $PadSettings   Group ID/Name: $PadGroupName   Users to Process: 0 (unmatched users not being removed)" -Encoding Unicode  
								}							
							}

							# Add people from $UsersToProcess.EmailAddress to the group $TargetDistributionGroup where they are in the filtered list $OnlineUsers.UserPrincipalName AND NOT already in the $ExistingMember.EmailAddress
							[int]$ResultCount=0
							[int]$personprocessed=0
							$funcstarttime = (Get-Date)
							
							foreach ($User in $UsersToProcess) {
								$ResultCount = $ResultCount + 1
								$personprocessed = $personprocessed + 1
								$GroupContainsUser = $false
								
								if ($ExistingMembers.ExternalDirectoryObjectId -contains $User.ObjectID) { $GroupContainsUser = $true } # 1.0.84

								if ( ( $FilteringEnabled -eq $false -or $OnlineUsers.($ObjectField) -contains $User.ObjectID) -and $GroupContainsUser -eq $false -and ($Group.HasGroupRules -eq $false -or ($GroupMatchedUsers.ObjectID -contains $User.ObjectID))) { # 1.0.84
								
									if ($Group.HasADOURules -eq $true) {
										$OU = $null
										$OU = ($AzureUsers | where {$_.ObjectID -eq $User.ObjectID} | Select -First 1).ExtensionProperty.onPremisesDistinguishedName
									}

									if ($Group.HasADOURules -eq $false -or ( (Check_ADCanonicalName -UserCN $OU -RequiredOrOUs $Group.RequiredOrOUs -NotRequiredOUs $Group.NotRequiredOUs) -eq $true)) {
										
										if ($Group.CollectionID -eq 0 -or ( @($UserOutput | where {$_.CollectionID -gt $Group.CollectionIDIgnoreThreshold -and $_.CollectionID -lt $Group.CollectionID -and $_.CollectionID -notin @($Group.CollectionIDsToExclude) -and $_.UserID -eq $User.ObjectID -and ($_.UserType -eq $Group.UserType -or $IgnoreCollectionIDUserType -eq $true) -and $_.State -gt 1 }).count -eq 0)) { #1.0.86
											$UsersAdded +=1

											if ($UsersAdded -eq 1) {
												$stringb = $user.EmailAddress
												$stringb = ", including: $stringb"
											}

											$ErrorGenerated=$false
											$errorstring = ""
											if ($MakeChanges -eq $true) {
												$e = $error.count
												Add-DistributionGroupMember -identity $TargetDistributionGroup.ExternalDirectoryObjectId -Member $User.ObjectID -Confirm:$false
												if ($error.count -gt $e) {
													if ($Error[0].Exception.Message) {
														$errorstring = $Error[0].Exception.Message
													} else {
														$errorstring = "(unknown)"
													}
													$ErrorGenerated=$true
													
													if ( ($Group.OnFailureApplytoAllUsersOnNextRun -band 2) -eq 2 -or ($GlobalOnFailureApplytoAllUsersOnNextRun -band 2) -eq 2) {
														if ($Group.UserType -eq 'Teacher') {
															$ApplytoAllTeachersOnNextRunThisFunc+=1
														} else {
															$ApplytoAllStudentsOnNextRunThisFunc+=1		
														}
													}
												}
											}

											$PendingRemovals = @($PendingRemovals | where { $_.UserID -ne $User.ObjectID -or $_.GroupExDirObjectID -ne $TargetDistributionGroup.ExternalDirectoryObjectId} )
											# $PendingRemovals = @($PendingRemovals | where { !($_.UserID -eq $User.ObjectID -and $_.GroupExDirObjectID -eq $TargetDistributionGroup.ExternalDirectoryObjectId)} ) # alt
									
											$UserOutput += [PSCustomObject]@{
												'UserType' = $Group.UserType
												'Settings' = $Group.Settings
												#'Default' = $Group.Default
												'CollectionID' = $Group.CollectionID
												#'CollectionIDIgnoreThreshold' = $Group.CollectionIDIgnoreThreshold
												'DisplayName' = $user.DisplayName
												'EmailAddress' = $user.EmailAddress
												'Action' = "Added To Group"
												'GroupName' = $TargetDistributionGroup.DisplayName
												'UserID' = $User.ObjectID
												'GroupID' = $TargetDistributionGroup.Id
												'GroupGuid' = $TargetDistributionGroup.Guid.Guid
												'GroupExDirObjectID' = $TargetDistributionGroup.ExternalDirectoryObjectId
												'GroupType' = $TargetDistributionGroup.GroupType
												'GroupEmail' = $TargetDistributionGroup.PrimarySMTPAddress
												'Simulation' = !$MakeChanges
												'Filter' = $Group.ActionFilter
												'NumberofFilteredUsers' = $OnlineUsers.Count
												'RemoveUnMatchedMembers' = $UnMatchedMembers
												'OnlyRemoveSameType' = $Group.OnlyRemoveSameType
												'SettingsNumber' = $Group.SettingsNumber
												'SettingsDescription' = $Group.Description
												'State' = 2
												'ErrorGenerated' = $ErrorGenerated
												'ErrorMessage' = $errorstring
											}
										} else {
											$UserOutput += [PSCustomObject]@{	
												'UserType' = $Group.UserType
												'Settings' = $Group.Settings
												#'Default' = $Group.Default
												'CollectionID' = $Group.CollectionID
												#'CollectionIDIgnoreThreshold' = $Group.CollectionIDIgnoreThreshold
												'DisplayName' = $user.DisplayName
												'EmailAddress' = $user.EmailAddress
												'Action' = "User filtered by CollectionID"
												'GroupName' = $TargetDistributionGroup.DisplayName
												'UserID' = $User.ObjectID
												'GroupID' = $TargetDistributionGroup.Id
												'GroupGuid' = $TargetDistributionGroup.Guid.Guid
												'GroupExDirObjectID' = $TargetDistributionGroup.ExternalDirectoryObjectId
												'GroupType' = $TargetDistributionGroup.GroupType
												'GroupEmail' = $TargetDistributionGroup.PrimarySMTPAddress
												'Simulation' = !$MakeChanges
												'Filter' = $Group.ActionFilter
												'NumberofFilteredUsers' = $OnlineUsers.Count
												'RemoveUnMatchedMembers' = $UnMatchedMembers
												'OnlyRemoveSameType' = $Group.OnlyRemoveSameType
												'SettingsNumber' = $Group.SettingsNumber
												'SettingsDescription' = $Group.Description
												'State' = -3
												'ErrorGenerated' = '-'
												'ErrorMessage' = '-'	
											}
										}
									} else {

										$UserOutput += [PSCustomObject]@{
											'UserType' = $Group.UserType
											'Settings' = $Group.Settings
											#'Default' = $Group.Default
											'CollectionID' = $Group.CollectionID
											#'CollectionIDIgnoreThreshold' = $Group.CollectionIDIgnoreThreshold
											'DisplayName' = $user.DisplayName
											'EmailAddress' = $user.EmailAddress
											'Action' = "User Filtered by AD OU/CN Rules"
											'GroupName' = $TargetDistributionGroup.DisplayName
											'UserID' = $User.ObjectID
											'GroupID' = $TargetDistributionGroup.Id
											'GroupGuid' = $TargetDistributionGroup.Guid.Guid
											'GroupExDirObjectID' = $TargetDistributionGroup.ExternalDirectoryObjectId
											'GroupType' = $TargetDistributionGroup.GroupType
											'GroupEmail' = $TargetDistributionGroup.PrimarySMTPAddress
											'Simulation' = !$MakeChanges
											'Filter' = $Group.ActionFilter
											'NumberofFilteredUsers' = $OnlineUsers.Count
											'RemoveUnMatchedMembers' = $UnMatchedMembers
											'OnlyRemoveSameType' = $Group.OnlyRemoveSameType
											'SettingsNumber' = $Group.SettingsNumber
											'SettingsDescription' = $Group.Description
											'State' = 0
											'ErrorGenerated' = '-'
											'ErrorMessage' = '-'
										}
									}									
								} else {
									
									
									if ($GroupContainsUser -eq $false) {
										 $UserOutput += [PSCustomObject]@{
											'UserType' = $Group.UserType
											'Settings' = $Group.Settings
											#'Default' = $Group.Default
											'CollectionID' = $Group.CollectionID
											#'CollectionIDIgnoreThreshold' = $Group.CollectionIDIgnoreThreshold
											'DisplayName' = $user.DisplayName
											'EmailAddress' = $user.EmailAddress
											'Action' = "User Filtered by Group Rules or $($Group.FilterSource)"
											'GroupName' = $TargetDistributionGroup.DisplayName
											'UserID' = $User.ObjectID
											'GroupID' = $TargetDistributionGroup.Id
											'GroupGuid' = $TargetDistributionGroup.Guid.Guid
											'GroupExDirObjectID' = $TargetDistributionGroup.ExternalDirectoryObjectId
											'GroupType' = $TargetDistributionGroup.GroupType
											'GroupEmail' = $TargetDistributionGroup.PrimarySMTPAddress
											'Simulation' = !$MakeChanges
											'Filter' = $Group.ActionFilter
											'NumberofFilteredUsers' = $OnlineUsers.Count
											'RemoveUnMatchedMembers' = $UnMatchedMembers
											'OnlyRemoveSameType' = $Group.OnlyRemoveSameType
											'SettingsNumber' = $Group.SettingsNumber
											'SettingsDescription' = $Group.Description
											'State' = 0
											'ErrorGenerated' = '-'
											'ErrorMessage' = '-'
										 }
									} else {
										 $UserOutput += [PSCustomObject]@{
											'UserType' = $Group.UserType
											'Settings' = $Group.Settings
											#'Default' = $Group.Default
											'CollectionID' = $Group.CollectionID
											#'CollectionIDIgnoreThreshold' = $Group.CollectionIDIgnoreThreshold
											'DisplayName' = $user.DisplayName
											'EmailAddress' = $user.EmailAddress
											'Action' = "Group contains User"
											'GroupName' = $TargetDistributionGroup.DisplayName
											'UserID' = $User.ObjectID
											'GroupID' = $TargetDistributionGroup.Id
											'GroupGuid' = $TargetDistributionGroup.Guid.Guid
											'GroupExDirObjectID' = $TargetDistributionGroup.ExternalDirectoryObjectId
											'GroupType' = $TargetDistributionGroup.GroupType
											'GroupEmail' = $TargetDistributionGroup.PrimarySMTPAddress
											'Simulation' = !$MakeChanges
											'Filter' = $Group.ActionFilter
											'NumberofFilteredUsers' = $OnlineUsers.Count
											'RemoveUnMatchedMembers' = $UnMatchedMembers
											'OnlyRemoveSameType' = $Group.OnlyRemoveSameType
											'SettingsNumber' = $Group.SettingsNumber
											'SettingsDescription' = $Group.Description
											'State' = 3
											'ErrorGenerated' = '-'
											'ErrorMessage' = '-'
										 }									
									}								
								}
								if ($ResultCount -eq $LongJobUpdateInterval) {
									[int]$ResultCount=0
									
									$UsersProcessedString, $timestring, $timeremainingseconds = get-timeelapsed -StartTime $funcstarttime -Processed $personprocessed -ToProcess ($UsersToProcess.Count)
									
									if ($timeremainingseconds -ge 10) {

										write-host "$($simstring)Processing Filtered Distribution Group User Additions - CollectionID: $PadCollectionID   $PadSettings   Group ID/Name: $PadGroupName   Processed $UsersProcessedString of $($UsersToProcess.Count) potential users - ETR$timestring" -ForegroundColor Gray

										if ($DetailedLoggingEnabled) {
											Add-Content -Path "$LogFile" -Value "$($simstring)Processing Filtered Distribution Group User Additions - CollectionID: $PadCollectionID   $PadSettings   Group ID/Name: $PadGroupName   Processed $UsersProcessedString of $($UsersToProcess.Count) potential users - ETR$timestring" -Encoding Unicode  
										}
									}
								}	
							}

							if ($UsersAdded -gt 0) {


								$Output += [PSCustomObject]@{
									'UserType' = $Group.UserType
									'Settings' = $Group.Settings
									#'Default' = $Group.Default
									'CollectionID' = $Group.CollectionID
									#'CollectionIDIgnoreThreshold' = $Group.CollectionIDIgnoreThreshold
									#'CollectionIDsToExclude' = $Group.CollectionIDsToExclude
									'GroupName' = $TargetDistributionGroup.DisplayName
									'Simulation' = !$MakeChanges
									'Status' = "Users Added to Group"
									'UsersModified' = $UsersAdded
									#'Description' = $Group.Description
									'Filter' = $Group.ActionFilter
									'GroupID' = $TargetDistributionGroup.Id
									'GroupGuid' = $TargetDistributionGroup.Guid.Guid
									'GroupExDirObjectID' = $TargetDistributionGroup.ExternalDirectoryObjectId
									'GroupType' = $TargetDistributionGroup.GroupType
									'GroupEmail' = $TargetDistributionGroup.PrimarySMTPAddress
									'SettingsNumber' = $Group.SettingsNumber
									'SettingsDescription' = $Group.Description
								}
							}


			#$onlineusers | where { $_.UserPrincipalName -eq 'upn@school.org.uk' } | fl * << ObjectID
			#$existingMembers | where { $_.PrimarySMTPAddress -eq 'upn@school.org.uk' } | fl * << ExternalDirectoryObjectId 
			#$UsersToProcess | where { $_.EmailAddress -eq 'upn@school.org.uk' } | fl *    << ObjectID

							$PadUsersAdded=[string]$UsersAdded
							$PadUsersAdded = $PadUsersAdded.PadLeft(4," ")

							$PadUsersPendingRemoval=[string]$PendingRemovalThisGroup
							$PadUsersPendingRemoval = $PadUsersPendingRemoval.PadLeft(4," ")


							if ($Verbose -eq $true) {   
								write-host "$($SimString)Updated Membership - CollectionID: $PadCollectionID   $PadSettings   Group ID/Name: $PadGroupName             Added Users: $PadUsersAdded$stringb" -ForegroundColor $textcolour
								write-host "$($SimString)Updated Membership - CollectionID: $PadCollectionID   $PadSettings   Group ID/Name: $PadGroupName   Users Pending Removal: $PadUsersPendingRemoval$string" -ForegroundColor $textcolour							
							}
							if ($DetailedLoggingEnabled) {
								Add-Content -Path "$LogFile" -Value "$($SimString)Updated Membership - CollectionID: $PadCollectionID   $PadSettings   Group ID/Name: $PadGroupName             Added Users: $PadUsersAdded$stringb" -Encoding Unicode
								Add-Content -Path "$LogFile" -Value "$($SimString)Updated Membership - CollectionID: $PadCollectionID   $PadSettings   Group ID/Name: $PadGroupName   Users Pending Removal: $PadUsersPendingRemoval$string" -Encoding Unicode							
							}

						} else {
						
							$Filter = $null
							$OnlineUsers = @()
						
							if ($Error[0].Exception.Message) {
								$errorstring = $Error[0].Exception.Message
							} else {
								$errorstring = "(unknown)"
							}

							$Output += [PSCustomObject]@{
								'UserType' = $Group.UserType
								'Settings' = $Group.Settings
								#'Default' = $Group.Default
								'CollectionID' = $Group.CollectionID
								#'CollectionIDIgnoreThreshold' = $Group.CollectionIDIgnoreThreshold
								#'CollectionIDsToExclude' = $Group.CollectionIDsToExclude
								'GroupName' = $TargetDistributionGroup.DisplayName
								'Simulation' = !$MakeChanges
								'Status' = "Unable to get $FilterType $FilterSource Users - error message: " + $errorstring 
								'UsersModified' = 0
								#'Description' = $Group.Description
								'Filter' = $Group.ActionFilter
								'GroupID' = $TargetDistributionGroup.Id
								'GroupGuid' = $TargetDistributionGroup.Guid.Guid
								'GroupExDirObjectID' = $TargetDistributionGroup.ExternalDirectoryObjectId
								'GroupType' = $TargetDistributionGroup.GroupType
								'GroupEmail' = $TargetDistributionGroup.PrimarySMTPAddress
								'SettingsNumber' = $Group.SettingsNumber
								'SettingsDescription' = $Group.Description
							}

							$PadUsersNotProcessed=[string]$UsersToProcess.count
							$PadUsersNotProcessed = $PadUsersNotProcessed.PadRight(4," ")


							write-host "There was an error in retrieving $FilterType $FilterSource Users - CollectionID: $PadCollectionID   $PadSettings   Group ID/Name: $PadGroupName   Users Not Processed: $PadUsersNotProcessed" -ForegroundColor Red
							$warnings += "- There was an error in retrieving $FilterType $FilterSource Users - CollectionID: $PadCollectionID   $PadSettings   Group ID/Name: $PadGroupName   Users Not Processed: $PadUsersNotProcessed"
							if ($DetailedLoggingEnabled) {
								Add-Content -Path "$LogFile" -Value "There was an error in retrieving $FilterType $FilterSource Users - CollectionID: $PadCollectionID   $PadSettings   Group ID/Name: $PadGroupName   Users Not Processed: $PadUsersNotProcessed" -Encoding Unicode
							}
						}

					} else {
					# group filter means no users can be processed
						$Output += [PSCustomObject]@{
							'UserType' = $Group.UserType
							'Settings' = $Group.Settings
							#'Default' = $Group.Default
							'CollectionID' = $Group.CollectionID
							#'CollectionIDIgnoreThreshold' = $Group.CollectionIDIgnoreThreshold
							#'CollectionIDsToExclude' = $Group.CollectionIDsToExclude
							'GroupName' = $TargetDistributionGroup.DisplayName
							'Simulation' = !$MakeChanges
							'Status' = "Group Rules Impossible"
							'UsersModified' = 0
							#'Description' = $Group.Description
							'Filter' = $Group.Filter
							'GroupID' = $TargetDistributionGroup.Id
							'GroupGuid' = $TargetDistributionGroup.Guid.Guid
							'GroupExDirObjectID' = $TargetDistributionGroup.ExternalDirectoryObjectId
							'GroupType' = $TargetDistributionGroup.GroupType
							'GroupEmail' = $TargetDistributionGroup.PrimarySMTPAddress
							'SettingsNumber' = $Group.SettingsNumber
							'SettingsDescription' = $Group.Description
						}

						$PadUsersNotProcessed=[string]$UsersToProcess.count
						$PadUsersNotProcessed = $PadUsersNotProcessed.PadRight(4," ")

						write-host "Group Rules Impossible - CollectionID: $PadCollectionID   $PadSettings   Group ID/Name: $PadGroupName   Users Not Processed: $PadUsersNotProcessed" -ForegroundColor Yellow
						$warnings += "- Group Rules Impossible - CollectionID: $PadCollectionID   $PadSettings   Group ID/Name: $PadGroupName   Users Not Processed: $PadUsersNotProcessed"
						if ($DetailedLoggingEnabled) {
							Add-Content -Path "$LogFile" -Value "Group Rules Impossible - CollectionID: $PadCollectionID   $PadSettings   Group ID/Name: $PadGroupName   Users Not Processed: $PadUsersNotProcessed" -Encoding Unicode
						}
					}
                } else {
                # group filter means no users can be processed
					$Output += [PSCustomObject]@{
						'UserType' = $Group.UserType
						'Settings' = $Group.Settings
						#'Default' = $Group.Default
						'CollectionID' = $Group.CollectionID
						#'CollectionIDIgnoreThreshold' = $Group.CollectionIDIgnoreThreshold
						#'CollectionIDsToExclude' = $Group.CollectionIDsToExclude
						'GroupName' = $TargetDistributionGroup.DisplayName
						'Simulation' = !$MakeChanges
						'Status' = "Error retrieving Group Rules Users"
						'UsersModified' = 0
						#'Description' = $Group.Description
						'Filter' = $Group.Filter
						'GroupID' = $TargetDistributionGroup.Id
						'GroupGuid' = $TargetDistributionGroup.Guid.Guid
						'GroupExDirObjectID' = $TargetDistributionGroup.ExternalDirectoryObjectId
						'GroupType' = $TargetDistributionGroup.GroupType
						'GroupEmail' = $TargetDistributionGroup.PrimarySMTPAddress
						'SettingsNumber' = $Group.SettingsNumber
						'SettingsDescription' = $Group.Description
					}

					$PadUsersNotProcessed=[string]$UsersToProcess.count
					$PadUsersNotProcessed = $PadUsersNotProcessed.PadRight(4," ")

					write-host "Error retrieving Group Rules Users: CollectionID: $PadCollectionID   $PadSettings   Group ID/Name: $PadGroupName   Users Not Processed: $PadUsersNotProcessed" -ForegroundColor Yellow
					$warnings += "- Error retrieving Group Rules Users: CollectionID: $PadCollectionID   $PadSettings   Group ID/Name: $PadGroupName   Users Not Processed: $PadUsersNotProcessed"
					if ($DetailedLoggingEnabled) {
						Add-Content -Path "$LogFile" -Value "Error retrieving Group Rules Users: CollectionID: $PadCollectionID   $PadSettings   Group ID/Name: $PadGroupName   Users Not Processed: $PadUsersNotProcessed" -Encoding Unicode
					}
                }				
# end 1.0.21
            }
            if ( @($TargetDistributionGroup).count -eq 0 -or !$TargetDistributionGroup) {
				if ( ($Group.OnFailureApplytoAllUsersOnNextRun -band 4) -eq 4 -or ($GlobalOnFailureApplytoAllUsersOnNextRun -band 2) -eq 2) { # 1.0.79
					if ($Group.UserType -eq 'Teacher') {
						$ApplytoAllTeachersOnNextRunThisFunc+=$UsersToProcess.count
					} else {
						$ApplytoAllStudentsOnNextRunThisFunc+=$UsersToProcess.count		
					}
				}
                if ($Group.GroupNameAsID -eq $true) {
                    $Output += [PSCustomObject]@{
                        'UserType' = $Group.UserType
						'Settings' = $Group.Settings
						#'Default' = $Group.Default
						'CollectionID' = $Group.CollectionID
						#'CollectionIDIgnoreThreshold' = $Group.CollectionIDIgnoreThreshold
						#'CollectionIDsToExclude' = $Group.CollectionIDsToExclude
                        'GroupName' = ''
                        'Simulation' = !$MakeChanges
                        'Status' = "Group Missing"
                        'UsersModified' = 0
                        #'Description' = $Group.Description
                        'Filter' = $Group.Filter
						'GroupID' = ''
                        'GroupGuid' = $Group.GroupName
						'GroupExDirObjectID' = ''
						'GroupType' = ''
						'GroupEmail' = ''
						'SettingsNumber' = $Group.SettingsNumber
						'SettingsDescription' = $Group.Description
                    }
                } else {
                    $Output += [PSCustomObject]@{
                        'UserType' = $Group.UserType
						'Settings' = $Group.Settings
						#'Default' = $Group.Default
						'CollectionID' = $Group.CollectionID
						#'CollectionIDIgnoreThreshold' = $Group.CollectionIDIgnoreThreshold
						#'CollectionIDsToExclude' = $Group.CollectionIDsToExclude
                        'GroupName' = $Group.GroupName
                        'Simulation' = !$MakeChanges
                        'Status' = "Group Missing"
                        'UsersModified' = 0
                        #'Description' = $Group.Description
                        'Filter' = $Group.Filter
						'GroupID' = ''
                        'GroupGuid' = ''
						'GroupExDirObjectID' = ''
						'GroupType' = ''
						'GroupEmail' = ''
						'SettingsNumber' = $Group.SettingsNumber
						'SettingsDescription' = $Group.Description
                    }
                }

                $PadUsersNotProcessed=[string]$UsersToProcess.count
                $PadUsersNotProcessed = $PadUsersNotProcessed.PadRight(4," ")


                write-host "Distribution Group/ID Not Found - CollectionID: $PadCollectionID   $PadSettings   Group ID/Name: $PadGroupName   Users Not Processed: $PadUsersNotProcessed" -ForegroundColor Yellow
                $warnings += "- Distribution Group/ID Not Found - CollectionID: $PadCollectionID   $PadSettings   Group ID/Name: $PadGroupName   Users Not Processed: $PadUsersNotProcessed"
                if ($DetailedLoggingEnabled) {
                    Add-Content -Path "$LogFile" -Value "Distribution Group/ID Not Found - CollectionID: $PadCollectionID   $PadSettings   Group ID/Name: $PadGroupName   Users Not Processed: $PadUsersNotProcessed" -Encoding Unicode
                }
            }


            if ( @($TargetDistributionGroup).count -gt 1) {
				if ( ($Group.OnFailureApplytoAllUsersOnNextRun -band 4) -eq 4 -or ($GlobalOnFailureApplytoAllUsersOnNextRun -band 2) -eq 2) { # 1.0.79
					if ($Group.UserType -eq 'Teacher') {
						$ApplytoAllTeachersOnNextRunThisFunc+=$UsersToProcess.count
					} else {
						$ApplytoAllStudentsOnNextRunThisFunc+=$UsersToProcess.count		
					}
				}
                if ($Group.GroupNameAsID -eq $true) {
                        $Output += [PSCustomObject]@{
                        'UserType' = $Group.UserType
						'Settings' = $Group.Settings
						#'Default' = $Group.Default
						'CollectionID' = $Group.CollectionID
						#'CollectionIDIgnoreThreshold' = $Group.CollectionIDIgnoreThreshold
						#'CollectionIDsToExclude' = $Group.CollectionIDsToExclude
                        'GroupName' = ''
                        'Simulation' = !$MakeChanges
                        'Status' = "More than 1 group with that ID"
                        'UsersModified' = 0
                        #'Description' = $Group.Description
                        'Filter' = $Group.Filter
						'GroupID' = ''
                        'GroupGuid' = $Group.GroupName
						'GroupExDirObjectID' = ''
						'GroupType' = ''
						'GroupEmail' = ''
						'SettingsNumber' = $Group.SettingsNumber
						'SettingsDescription' = $Group.Description
                    }
                } else {
                    $Output += [PSCustomObject]@{
                        'UserType' = $Group.UserType
                        'Settings' = $Group.Settings						
						#'Default' = $Group.Default
						'CollectionID' = $Group.CollectionID
						#'CollectionIDIgnoreThreshold' = $Group.CollectionIDIgnoreThreshold
						#'CollectionIDsToExclude' = $Group.CollectionIDsToExclude
                        'GroupName' = $Group.GroupName
                        'Simulation' = !$MakeChanges
                        'Status' = "More than 1 group with that name"
                        'UsersModified' = 0
                        #'Description' = $Group.Description
                        'Filter' = $Group.Filter
						'GroupID' = ''
                        'GroupGuid' = ''
						'GroupExDirObjectID' = ''
						'GroupType' = ''
						'GroupEmail' = ''
						'SettingsNumber' = $Group.SettingsNumber
						'SettingsDescription' = $Group.Description
                    }
                }

                $PadUsersNotProcessed=[string]$UsersToProcess.count
                $PadUsersNotProcessed = $PadUsersNotProcessed.PadRight(4," ")


                write-host "More than 1 Matching Distribution Group Found - CollectionID: $PadCollectionID   $PadSettings   Group ID/Name: $PadGroupName   Users Not Processed: $PadUsersNotProcessed" -ForegroundColor Yellow
                $warnings += "- More than 1 Matching Distribution Group Found - CollectionID: $PadCollectionID   $PadSettings   Group ID/Name: $PadGroupName   Users Not Processed: $PadUsersNotProcessed"
                if ($DetailedLoggingEnabled) {
                    Add-Content -Path "$LogFile" -Value "More than 1 Matching Distribution Group Found - CollectionID: $PadCollectionID   $PadSettings   Group ID/Name: $PadGroupName   Users Not Processed: $PadUsersNotProcessed" -Encoding Unicode
                }
            }
        } # end for group loop

		if ( @($PendingRemovals).Count -gt 0) {
			[int]$GroupsProcessed=0
			[int]$ResultCount=0
            $funcstarttime = (Get-Date)
			write-host "$($SimString)Processing Accumulated Filtered Distribution Group User Removals - Users Pending Removal across all Groups: $($PendingRemovals.Count)" -ForegroundColor $textcolour
			if ($DetailedLoggingEnabled) {
                    Add-Content -Path "$LogFile" -Value "$($SimString)Processing Accumulated Filtered Distribution Group User Removals - Users Pending Removal across all Groups: $($PendingRemovals.Count)" -Encoding Unicode
            }
			[int]$TotalUsersRemoved=0
			foreach ($Group in $FilteredDistributionGroupsToProcess) {
				$GroupsProcessed = $GroupsProcessed +1
				[int]$UsersRemoved=0
				$FilteredPendingRemovals = @($PendingRemovals | where {$_.UserType -eq $Group.UserType -and $_.Settings -eq $Group.Settings -and $_.State -eq 1})
				
				if ($Group.Description) {
					$DescString = "$($Group.Settings) $($Group.Description)"
				} else {
					$DescString = "$($Group.Settings)"
				}

				$ProcessedString = [string]$GroupsProcessed
				$ProcessedString = $ProcessedString.PadLeft(($FilteredSecurityGroupsToProcess.Count -as [string]).length," ")

				if ( $FilteredPendingRemovals.Count -gt 0) {

					$string = $FilteredPendingRemovals[0].EmailAddress
					$string = ", including: $string"
				} else {
					$string = ''
				}
				$PadGroupName=$Group.GroupName.PadRight($GroupGroupNameMaxLength," ")
				$PadSettings=$Group.Settings.PadRight($SettingsMaxLength," ")
				$PadCollectionID=([string]$Group.CollectionID).PadRight($CollectionIDMaxLength," ")
				
				$PadUsersPendingRemoval=[string]$FilteredPendingRemovals.Count
                $PadUsersPendingRemoval = $PadUsersPendingRemoval.PadLeft(4," ")

				write-host "$($SimString)Processing Group $ProcessedString of $($FilteredDistributionGroupsToProcess.Count): CollectionID: $PadCollectionID   $PadSettings   Group ID/Name: $PadGroupName   Users Being Removed: $PadUsersPendingRemoval$string" -ForegroundColor $textcolour

				if ($DetailedLoggingEnabled) {
					Add-Content -Path "$LogFile" -Value "`r`n$($SimString)Processing Group $ProcessedString of $($FilteredDistributionGroupsToProcess.Count): CollectionID: $PadCollectionID   $PadSettings   Group ID/Name: $PadGroupName   Users Being Removed: $PadUsersPendingRemoval$string" -Encoding Unicode
				}

				if ( $FilteredPendingRemovals.Count -gt 0) {
					
					foreach ($PendingUser in $FilteredPendingRemovals) {
						$ResultCount = $ResultCount + 1
						$UsersRemoved+=1

						$ErrorGenerated=$false
						$errorstring = ""
						if ($MakeChanges -eq $true) {
							$e = $error.count

							#Remove-DistributionGroupMember -identity $PendingUser.GroupExDirObjectID -Member $PendingUser.EmailAddress -Confirm:$false
							Remove-DistributionGroupMember -identity $PendingUser.GroupExDirObjectID -Member $PendingUser.UserID -Confirm:$false
							if ($error.count -gt $e) {
								if ($Error[0].Exception.Message) {
									$errorstring = $Error[0].Exception.Message
								} else {
									$errorstring = "(unknown)"
								}
							$ErrorGenerated=$true
							}
						} 

						$UserOutput += [PSCustomObject]@{
							'UserType' = $PendingUser.UserType
							'Settings' = $PendingUser.Settings
							#'Default' = $Group.Default
							'CollectionID' = $PendingUser.CollectionID
							#'CollectionIDIgnoreThreshold' = $PendingUser.CollectionIDIgnoreThreshold
							'DisplayName' = $PendingUser.DisplayName
							'EmailAddress' = $PendingUser.EmailAddress
							'Action' = "Removed From Group"
							'GroupName' = $PendingUser.GroupName
							'UserID' = $PendingUser.UserID
							'GroupID' = $PendingUser.GroupID
							'GroupGuid' = $PendingUser.GroupGuid
							'GroupExDirObjectID' = $PendingUser.GroupExDirObjectID
							'GroupDescription' = $PendingUser.GroupDescription
							'Simulation' = !$MakeChanges
							'Filter' = $PendingUser.Filter
							'NumberofFilteredUsers' = $PendingUser.NumberofFilteredUsers
							'RemoveUnMatchedMembers' = $PendingUser.RemoveUnMatchedMembers
							'OnlyRemoveSameType' = $PendingUser.OnlyRemoveSameType
							'SettingsNumber' = $PendingUser.SettingsNumber
							'SettingsDescription' = $PendingUser.SettingsDescription
							'State' = 1
							'ErrorGenerated' = $ErrorGenerated
							'ErrorMessage' = $errorstring
						}
						if ($ResultCount -eq $LongJobUpdateInterval) {
							[int]$ResultCount=0
							
							$UsersProcessedString, $timestring, $timeremainingseconds = get-timeelapsed -StartTime $funcstarttime -Processed ($TotalUsersRemoved+$UsersRemoved) -ToProcess ($PendingRemovals.Count)
							
							if ($timeremainingseconds -ge 10) {

								write-host "$($simstring)Processing Accumulated Filtered Distribution Group User Removals - Processed $UsersProcessedString of $($PendingRemovals.Count) users - ETR$timestring" -ForegroundColor Gray

								if ($DetailedLoggingEnabled) {
									Add-Content -Path "$LogFile" -Value "$($simstring)Processing Accumulated Filtered Distribution Group User Removals - Processed $UsersProcessedString of $($UsersToApply.Count) users - ETR$timestring" -Encoding Unicode  
								}
							}
						}	
					}

					$Output += [PSCustomObject]@{
						'UserType' = $Group.UserType
						'Settings' = $Group.Settings
						#'Default' = $Group.Default
						'CollectionID' = $Group.CollectionID
						#'CollectionIDIgnoreThreshold' = $Group.CollectionIDIgnoreThreshold
						#'CollectionIDsToExclude' = $Group.CollectionIDsToExclude
						'GroupName' = $FilteredPendingRemovals[0].GroupName
						'Simulation' = !$MakeChanges
						'Status' = "Users Removed From Group"
						'UsersModified' = $UsersRemoved
						#'Description' = $Group.Description
						'Filter' = $Group.ActionFilter
						'GroupID' = $FilteredPendingRemovals[0].GroupID
						'GroupGuid' = $FilteredPendingRemovals[0].GroupGuid
						'GroupExDirObjectID' = $FilteredPendingRemovals[0].GroupExDirObjectID
						'GroupDescription' = $FilteredPendingRemovals[0].GroupDescription
						'SettingsNumber' = $FilteredPendingRemovals[0].SettingsNumber
						'SettingsDescription' = $FilteredPendingRemovals[0].SettingsDescription
					}
				$TotalUsersRemoved+=$UsersRemoved
				}	
			}
			if ($TotalUsersRemoved -ne @($PendingRemovals).Count) {
				write-host "Filtered Distribution Group Processing Error: Total Users Removed: $UsersRemoved - Total Pending Removal: $($PendingRemovals.Count)" -ForegroundColor Red
				$MajorErrors += "- Filtered Distribution Group Processing Error: Total Users Removed: $UsersRemoved - Total Pending Removal: $($PendingRemovals.Count)"
				if ($DetailedLoggingEnabled) {
                    Add-Content -Path "$LogFile" -Value "Filtered Distribution Group Processing Error: Total Users Removed: $UsersRemoved - Total Pending Removal: $($PendingRemovals.Count)" -Encoding Unicode
                }
			}
		} else {
			write-host "$($SimString)Processing Accumulated Filtered Distribution Group User Removals - Users Pending Removal across all Groups: $($PendingRemovals.Count)" -ForegroundColor $textcolour
			if ($DetailedLoggingEnabled) {
                    Add-Content -Path "$LogFile" -Value "$($SimString)Processing Accumulated Filtered Distribution Group User Removals - Users Pending Removal across all Groups: $($PendingRemovals.Count)" -Encoding Unicode
            }	
		}

		$UserOutput = @($UserOutput | Sort @{Expression = "UserType"; Descending = $true},@{Expression = "CollectionID"; Descending = $false},@{Expression = "SettingsNumber"; Descending = $false},@{Expression = "State"; Descending = $false},@{Expression = "EmailAddress"; Descending = $false})
		$Output = @($Output | Sort @{Expression = "UserType"; Descending = $true},@{Expression = "CollectionID"; Descending = $false},@{Expression = "SettingsNumber"; Descending = $false})

        $TeacherGroupsProcessed = @($Output | where { $_.UserType -eq 'Teacher' })
        $TeacherQty = ($TeacherGroupsProcessed.usersmodified | Measure-Object -sum).sum
        $TeacherGroups = @($FilteredDistributionGroupsToProcess | where {$_.UserType -eq 'Teacher' }).count

        $results += [PSCustomObject]@{
            'Type' = "Filtered Distribution Group Processing - $($TeacherGroupsProcessed.Count) Teacher Distribution Groups"
		    'Quantity' = $TeacherQty
		    'Source' = "$TeacherGroups Groups"
            'Action' = if ($TeacherQty -gt 0) { "$($SimString)Group Membership Updated" } else { "$($SimString)Group Membership Checked" }
            'Target' = 'Teachers'
            'OtherInfo' = ''
            'AssignmentErrors' = @($UserOutput  | where { $_.UserType -eq 'Teacher' -and $ErrorGenerated -eq $true }).count
             }

        $StudentGroupsProcessed = @($Output | where { $_.UserType -eq 'Student' })
        $StudentQty = ($StudentGroupsProcessed.usersmodified | Measure-Object -sum).sum
        $StudentGroups = @($FilteredDistributionGroupsToProcess | where {$_.UserType -eq 'Student' }).count

        $results += [PSCustomObject]@{
            'Type' = "Filtered Distribution Group Processing - $($StudentGroupsProcessed.Count) Student Distribution Groups"
		    'Quantity' = $StudentQty
		    'Source' = "$StudentGroups Groups"
            'Action' = if ($StudentQty -gt 0) { "$($SimString)Group Membership Updated" } else { "$($SimString)Group Membership Checked" }
            'Target' = 'Students'
            'OtherInfo' = ''
            'AssignmentErrors' = @($UserOutput  | where { $_.UserType -eq 'Student' -and $ErrorGenerated -eq $true }).count
             }


        if ($DetailedLoggingEnabled) {
            Add-Content -Path "$LogFile" -Value "`r`nFiltered Distribution Group Results:" -Encoding Unicode
            Add-Content -Path "$LogFile" -Value "------------------------------------" -Encoding Unicode
        
            Process-UserCSVOutput ($UserOutput)
            $littleobj = @()
			$littleobj = @($UserOutput | where { $_.Action -eq 'Removed From Group' -or $_.Action -eq 'Added To Group'})
            Add-Content -Path "$LogFile" -Value "$($SimString)$($UserOutput.count) Filtered Distribution Group Membership Test operations with $($littleobj.count) Total User Membership Changes" -Encoding Unicode
			$littleobj = @($littleobj  | Sort-Object -Property ObjectID -Unique) # 1.0.94
			Add-Content -Path "$LogFile" -Value "$($littleobj.count) Unique Users that required User Membership Changes`r`n" -Encoding Unicode
			
			$DefaultUserOutput = @($UserOutput | where { $_.Default -eq $true})
			$littleobj = @($DefaultUserOutput | where { $_.Action -eq 'Removed From Group' -or $_.Action -eq 'Added To Group'})
			Add-Content -Path "$LogFile" -Value "$($SimString)$($DefaultUserOutput.count) Filtered Distribution Group Add/Remove operations with $($littleobj.count) Total User Membership Changes under Default Processing" -Encoding Unicode
			$littleobj = @($littleobj  | Sort-Object -Property ObjectID -Unique) # 1.0.94
			Add-Content -Path "$LogFile" -Value "$($littleobj.count) Unique Users that required User Membership Changes under Default Processing`r`n" -Encoding Unicode
		
            Add-Content -Path "$LogFile" -Value "Group Results:" -Encoding Unicode
            Add-Content -Path "$LogFile" -Value "--------------" -Encoding Unicode
            $Output | ft * -AutoSize | Out-String -Width 4096 | Out-File -filepath "$LogFile" -append -Encoding Unicode
            Add-Content -Path "$LogFile" -Value "$($SimString)$($Output.Count) Filtered Distribution Group Add/Remove Batch Requests Processed with $TeacherQty Teacher Group Membership Changes and $StudentQty Student Group Membership Changes`r`n" -Encoding Unicode   


        }
        if ($MakeChanges -eq $true ) {
            $Actions += "- $($Output.Count) Filtered Distribution Group Add/Remove Batch Requests Processed with $TeacherQty Teacher Group Membership Changes and $StudentQty Student Group Membership Changes"
            write-host "$($Output.Count) Filtered Distribution Group Add/Remove Batch Requests Processed with $TeacherQty Teacher Group Membership Changes and $StudentQty Student Group Membership Changes" -ForegroundColor Yellow

            $TotalErrors = ($UserOutput | where { $_.ErrorGenerated -eq $true}).Count

            if ( $TotalErrors -gt 0) {
                if ($DetailedLoggingEnabled) {
			        Add-Content -Path "$LogFile" -Value "`r`nAdd/Remove Errors:" -Encoding Unicode
			        Add-Content -Path "$LogFile" -Value "------------------`r`n" -Encoding Unicode
    			    Add-Content -Path "$LogFile" -Value "$TotalErrors Group Add/Remove Errors were encountered during Filtered Distribution Group processing" -Encoding Unicode
                }
			    write-host "$TotalErrors Group Add/Remove Errors were encountered during Filtered Distribution Group processing" -ForegroundColor Red
			    $warnings += "- $TotalErrors Group Add/Remove Errors were encountered during Filtered Distribution Group processing"
		    }

        } else {
            $Actions += "- Simulation: $($Output.Count) Filtered Distribution Group Add/Remove Batch Requests Processed with $TeacherQty Teacher Group Membership Changes and $StudentQty Student Group Membership Changes"
            write-host "Simulation: $($Output.Count) Filtered Distribution Group Add/Remove Batch Requests Processed with $TeacherQty Teacher Group Membership Changes and $StudentQty Student Group Membership Changes" -ForegroundColor cyan
        }

		if ($ApplytoAllTeachersOnNextRunThisFunc -gt 0 -or $ApplytoAllStudentsOnNextRunThisFunc -gt 0) {
			$ApplytoAllonNextRunSummary += [PSCustomObject]@{
				'Function' = 'Filtered Distribution Group Processing'
				'Teachers' = $ApplytoAllTeachersOnNextRunThisFunc 
				'Students' = $ApplytoAllStudentsOnNextRunThisFunc
			}
		}
		if ($ApplytoAllTeachersOnNextRunThisFunc -gt 0) { $ApplytoAllTeachersOnNextRun=$true }
		if ($ApplytoAllStudentsOnNextRunThisFunc -gt 0) { $ApplytoAllStudentsOnNextRun=$true }

    } else {
        write-host "There are no Filtered Distribution Groups to Apply" -foregroundcolor white
        $Actions += "- There are no Filtered Distribution Groups to Apply"
        if ($DetailedLoggingEnabled) {
            Add-Content -Path "$LogFile" -Value "`r`nApplying Filtered Distribution Groups" -Encoding Unicode
            Add-Content -Path "$LogFile" -Value "=====================================`r`n" -Encoding Unicode
            Add-Content -Path "$LogFile" -Value "Group Changes to Process:" -Encoding Unicode
            Add-Content -Path "$LogFile" -Value "-------------------------" -Encoding Unicode
            Add-Content -Path "$LogFile" -Value "`r`nThere are no Filtered Distribution Groups to Apply" -Encoding Unicode
        }
    }
} else {
	if ($DetailedLoggingEnabled) {
		Add-Content -Path "$LogFile" -Value "`r`nFiltered Distribution Groups" -Encoding Unicode
		Add-Content -Path "$LogFile" -Value "============================`r`n" -Encoding Unicode
        Add-Content -Path "$LogFile" -Value "Group Changes that would be Processed:" -Encoding Unicode
		Add-Content -Path "$LogFile" -Value "--------------------------------------" -Encoding Unicode
		if ($FilteredDistributionGroupsToProcess.Count -gt 0) {
			$FilteredDistributionGroupsToProcess | select -Property * -ExcludeProperty CollectionIDsToExclude | ft * -AutoSize | Out-String -Width 4096 | Out-File -filepath "$LogFile" -append -Encoding Unicode # 1.0.94
			Add-Content -Path "$LogFile" -Value "Note: If the global filter type setting is set to Remote, the filter source is forced to CSOnline`r`n" -Encoding Unicode
		} else {
			Add-Content -Path "$LogFile" -Value "`r`nThere are no Filtered Distribution Groups to Apply" -Encoding Unicode
		}
	}	
}



# sleep
[int]$SleepSeconds=0
if ($MajorErrors.Count -eq 0 -and $iniFile.Misc.SleepSecondsBeforeApplyingPolicies) {
    [int]$SleepSeconds = $iniFile.Misc.SleepSecondsBeforeApplyingPolicies -as [int]
    if ($SleepSeconds -gt 0) {
        write-host "Sleeping for $SleepSeconds seconds" -foreground green
        if ($DetailedLoggingEnabled) {
            Add-Content -Path "$LogFile" -Value "`r`nSleeping for $SleepSeconds seconds`r`n" -Encoding Unicode
        }
        Start-Sleep -S $SleepSeconds
    }
}

# Need to be Connected to Teams - this is done above
#exit 0

$UsersToDeLicense=@()

if ($MajorErrors.Count -eq 0 -and ($PackagesToProcess.Count -gt 0 -or $PoliciestoProcess.Count -gt 0)) {

    [int]$MaximumBatchSize=5000

    if ($iniFile.Misc.MaximumBatchSize) {
        [int]$MaximumBatchSize=$iniFile.Misc.MaximumBatchSize -as [int]
		if ($MaximumBatchSize -lt 20) { $MaximumBatchSize = 5000 }
	}        
    $Information+= "- Maximum Batch Sizes for Policy and Package Processing: $MaximumBatchSize"
}


# process packages
if ($MajorErrors.Count -eq 0) {
    $AssignmentErrorsCaptured=0
    $DisabledUsersErrorsCaptured=0
	[int]$ApplytoAllTeachersOnNextRunThisFunc=0
	[int]$ApplytoAllStudentsOnNextRunThisFunc=0
    if ($PackagesToProcess.Count -gt 0) {
        write-host "Applying Filtered Teams Packages" -foregroundcolor green        
        if ($DetailedLoggingEnabled) {
            Add-Content -Path "$LogFile" -Value "`r`nApplying Filtered Teams Packages" -Encoding Unicode
            Add-Content -Path "$LogFile" -Value "================================`r`n" -Encoding Unicode
            Add-Content -Path "$LogFile" -Value "Packages to Process:" -Encoding Unicode
            Add-Content -Path "$LogFile" -Value "--------------------" -Encoding Unicode
            $PackagesToProcess | select -Property * -ExcludeProperty CollectionIDsToExclude | ft * -AutoSize | Out-String -Width 4096 | Out-File -filepath "$LogFile" -append -Encoding Unicode # 1.0.94
			Add-Content -Path "$LogFile" -Value "Note: If the global filter type setting is set to Remote, the filter source is forced to CSOnline`r`n" -Encoding Unicode
        }

        [int]$TeacherTeamsPackageBatchProcessingThreshold=99999

        if ($iniFile.Misc.TeacherTeamsPackageBatchProcessingThreshold) {
            [int]$TeacherTeamsPackageBatchProcessingThreshold=$iniFile.Misc.TeacherTeamsPackageBatchProcessingThreshold -as [int]
		}
		if ($DetailedLoggingEnabled) {
            Add-Content -Path "$LogFile" -Value "Teacher Teams Package Batch Processing Threshold: $TeacherTeamsPackageBatchProcessingThreshold" -Encoding Unicode
        }
        $Information+= "- Teacher Teams Package Batch Processing Threshold: $TeacherTeamsPackageBatchProcessingThreshold"
        
        [int]$StudentTeamsPackageBatchProcessingThreshold=99999

        if ($iniFile.Misc.StudentTeamsPackageBatchProcessingThreshold) {
            [int]$StudentTeamsPackageBatchProcessingThreshold=$iniFile.Misc.StudentTeamsPackageBatchProcessingThreshold -as [int]
		}
		if ($DetailedLoggingEnabled) {
            Add-Content -Path "$LogFile" -Value "Student Teams Package Batch Processing Threshold: $StudentTeamsPackageBatchProcessingThreshold" -Encoding Unicode
        }
        $Information+= "- Student Teams Package Batch Processing Threshold: $StudentTeamsPackageBatchProcessingThreshold"
        
        if ($DetailedLoggingEnabled) {
            Add-Content -Path "$LogFile" -Value "Maximum Batch Sizes for Policy and Package Processing: $MaximumBatchSize" -Encoding Unicode
            Add-Content -Path "$LogFile" -Value "`r`n"
        }

        [int]$PackagesProcessed=0
        $Filter=$null
		$FilterSource=$null
        $OnlineUsers = @()
		
		$AllCSOnlineUsers = @()
		$AllAzureADUsers = @()
		
        $Output= @()
		$onlineobj = $null
		$AssignmentErrors = @()
        $UserOutput = @()

        [int]$PackagePackageMaxLength=($PackagesToProcess | Select Package | Sort -Property { $_.Package.length} | select -Last 1).Package.Length
		[int]$SettingsMaxLength=($PackagesToProcess | Select Settings | Sort -Property { $_.Settings.length} | select -Last 1).Settings.Length
		[int]$CollectionIDMaxLength=(($PackagesToProcess | Select CollectionID | Sort -Property { $_.CollectionID} | select -Last 1).CollectionID -as [string]).length
        
		$CurrentlyUnlicensedUsers = @()
		$CurrentlyUnlicensedUsers = Get-MsolUser -All -UnlicensedUsersOnly	
		
        foreach ($Package in $PackagesToProcess) {
            $UsersToApply = @()
            $AssignmentErrorsThisPackage=0
            $DisabledUsersThisPackage=0
            $PackagesProcessed = $PackagesProcessed +1

			$RemoveLGForAssignmentErrors = switch($Package.RemoveFailedUsersFromLicensingGroup -band 2) { 2 {$true}; default {$false}}
			$RemoveLGForDisabledUsers = switch($Package.RemoveFailedUsersFromLicensingGroup -band 4) { 4 {$true}; default {$false}}
			$RemoveLGForNotLicensedUsers = switch($Package.RemoveFailedUsersFromLicensingGroup -band 8) { 8 {$true}; default {$false}}

            if ($Package.Description) {
                $DescString = "$($Package.Description)"
            } else {
                $DescString = "-"
            }
            
            $ProcessedString = [string]$PackagesProcessed
			$ProcessedString = $ProcessedString.PadLeft(($PackagesToProcess.Count -as [string]).length," ")
			$PadSettings=$Package.Settings.PadRight($SettingsMaxLength," ")
			$PadCollectionID=([string]$Package.CollectionID).PadRight($CollectionIDMaxLength," ")

            $PadPackage=$Package.Package.PadRight($PackagePackageMaxLength," ")

            write-host "$($SimString)Processing Package Request $ProcessedString of $($PackagesToProcess.Count): CollectionID: $PadCollectionID   $PadSettings   Package Name: $PadPackage   Desc: $DescString" -ForegroundColor $textcolour

            if ($DetailedLoggingEnabled) {
                Add-Content -Path "$LogFile" -Value "`r`n$($SimString)Processing Package Request $ProcessedString of $($PackagesToProcess.Count): CollectionID: $PadCollectionID   $PadSettings   Package Name: $PadPackage   Desc: $DescString" -Encoding Unicode
            }

			$string=$null
			$string=""
            if ($Package.UserType -eq 'Student') {
                if ($Package.ApplyToAll -eq $true -or $ApplytoAllStudents -eq $true) {
                    $UsersToProcess = $StudentList
                } else {
                    $UsersToProcess = $StudentsNotInLicensingGroup
					$string=" that were not members of the Licensing Group"
                }
            }
            if ($Package.UserType -eq 'Teacher') {
                if ($Package.ApplyToAll -eq $true -or $ApplytoAllTeachers -eq $true) {
                    $UsersToProcess = $TeacherList
                } else {
                    $UsersToProcess = $TeachersNotInLicensingGroup
					$string=" that were not members of the Licensing Group"
                }
            }

			if ($Package.DomainFilter ) {
				$UsersToProcess = @($UsersToProcess | where { $Package.DomainFilter -contains $_.EmailAddress.Split('@')[1] })
				$string="$string and were filtered by the Domain Filter"
			}
			
            if ($Package.OnlyApplyToUnlicensed -eq $true) {
                $UsersToProcess = @($UsersToProcess | where { $_.IsLicensed -eq $false })
				write-host "$($SimString)Using $($UsersToProcess.count) Unlicensed Source List $($Package.UserType)s$String" -ForegroundColor $textcolour	
				if ($DetailedLoggingEnabled) {
					Add-Content -Path "$LogFile" -Value "$($SimString)Using $($UsersToProcess.count) Unlicensed Source List $($Package.UserType)s$String"
				}				
            } else {
				write-host "$($SimString)Using $($UsersToProcess.count) Source List $($Package.UserType)s$String" -ForegroundColor $textcolour	
				if ($DetailedLoggingEnabled) {
					Add-Content -Path "$LogFile" -Value "$($SimString)Using $($UsersToProcess.count) Source List $($Package.UserType)s$String"
				}	
			}
			$string=$null

            $RequiredRecursiveIDs = @()
            $NotRequiredRecursiveIDs = @()
            $ProblemGroups = @()
            $GroupRulesOK = $true
			$GotGroupUsersOK = $true
			$GroupMatchedUsers = @()
            if ($Package.HasGroupRules -eq $true) {
                write-host "$($SimString)Building Group Rules Table" -ForegroundColor $textcolour

                if ($DetailedLoggingEnabled) {
                    Add-Content -Path "$LogFile" -Value "`r`n$($SimString)Building Group Rules Table`r`n" -Encoding Unicode
                }
				$e = $error.count
                $GroupRulesOK, $ProblemGroups, $RequiredRecursiveIDs, $RequiredORRecursiveIDs, $NotRequiredRecursiveIDs = BuildGroupTable -GroupInformation $Package
                
				if ($GroupRulesOK -eq $true) {
					write-host "$($SimString)Group Rules OK: $GroupRulesOK   Problem Groups: $($ProblemGroups.count)   RequiredGroups: $($RequiredRecursiveIDs.count)   RequiredOrGroups: $($RequiredORRecursiveIDs.count)   NotRequiredGroups: $($NotRequiredRecursiveIDs.count)" -ForegroundColor $textcolour
					$GroupMatchedUsers = GetGroupRuleUsers -RequiredRecursiveIDs $RequiredRecursiveIDs -RequiredORRecursiveIDs $RequiredORRecursiveIDs -NotRequiredRecursiveIDs $NotRequiredRecursiveIDs
					write-host "$($SimString)Found $($GroupMatchedUsers.count) users matching the Group Rules" -ForegroundColor $textcolour
				} else {
					write-host "$($SimString)Group Rules OK: $GroupRulesOK   Problem Groups: $($ProblemGroups.count)   RequiredGroups: $($RequiredRecursiveIDs.count)   RequiredOrGroups: $($RequiredORRecursiveIDs.count)   NotRequiredGroups: $($NotRequiredRecursiveIDs.count)" -ForegroundColor yellow
					$warnings += "- $($SimString)Package $($PackagesProcessed): Group Rules OK: $GroupRulesOK   Problem Groups: $($ProblemGroups.count)   RequiredGroups: $($RequiredRecursiveIDs.count)   RequiredOrGroups: $($RequiredORRecursiveIDs.count)   NotRequiredGroups: $($NotRequiredRecursiveIDs.count)"
				}
				if ($error.count -gt $e) {
                    $errorstring = ""
			        if ($Error[0].Exception.Message) {
				        $errorstring = $Error[0].Exception.Message
			        } else {
				        $errorstring = "(unknown)"
			        }
					write-host "There was an error in processing Group Rules or matching users - error message: $errorstring" -foreground red
					if ($DetailedLoggingEnabled) {
						Add-Content -Path "$LogFile" -Value "There was an error in processing Group Rules or matching users - error message: $errorstring" -Encoding Unicode
					}
					$Warnings += "- $($SimString)Package $($PackagesProcessed): There was an error in processing Group Rules or matching users - error message: $errorstring"
					$GotGroupUsersOK = $false
                }
                                
                if ($DetailedLoggingEnabled) {
                    if ($RequiredRecursiveIDs.count -gt 0) {
						Add-Content -Path "$LogFile" -Value "Group Rules: RequiredGroups, Including Recursive"
                        Add-Content -Path "$LogFile" -Value "------------------------------------------------"
                        $RequiredRecursiveIDs | Sort DisplayName,  CommonName, Description, ObjectID | fl * | Out-String -Width 4096 | Out-File -filepath "$LogFile" -append -Encoding Unicode
                    }
                    if ($RequiredORRecursiveIDs.count -gt 0) {
						Add-Content -Path "$LogFile" -Value "Group Rules: RequiredOrGroups, Including Recursive"
                        Add-Content -Path "$LogFile" -Value "--------------------------------------------------"
                        $RequiredORRecursiveIDs | Sort DisplayName,  CommonName, Description, ObjectID | fl * | Out-String -Width 4096 | Out-File -filepath "$LogFile" -append -Encoding Unicode
                    }					
                    if ($NotRequiredRecursiveIDs.count -gt 0) {
						Add-Content -Path "$LogFile" -Value "Group Rules: NotRequiredGroups, Including Recursive"
                        Add-Content -Path "$LogFile" -Value "---------------------------------------------------"
                        $NotRequiredRecursiveIDs | Sort DisplayName,  CommonName, Description, ObjectID | fl * | Out-String -Width 4096 | Out-File -filepath "$LogFile" -append -Encoding Unicode
                    }
                    if ($ProblemGroups.count -gt 0) {
						Add-Content -Path "$LogFile" -Value "Group Rules: Problem Groups"
                        Add-Content -Path "$LogFile" -Value "---------------------------"
                        $ProblemGroups | Sort DisplayName,  CommonName, Description, ObjectID | fl * | Out-String -Width 4096 | Out-File -filepath "$LogFile" -append -Encoding Unicode
                    }
					Add-Content -Path "$LogFile" -Value "$($SimString)Group Rules OK: $GroupRulesOK   Problem Groups: $($ProblemGroups.count)   RequiredGroups: $($RequiredRecursiveIDs.count)   RequiredOrGroups: $($RequiredORRecursiveIDs.count)   NotRequiredGroups: $($NotRequiredRecursiveIDs.count)" -Encoding Unicode
					Add-Content -Path "$LogFile" -Value "$($SimString)Found $($GroupMatchedUsers.count) users matching the Group Rules" -Encoding Unicode					
                }
            }
# end 1.0.21

			if ($Package.HasADOURules -eq $true ) {
					write-host "$($SimString)Active Directory OU/CN Rules - Included OU/CN Or Rules: $($Package.RequiredOrOUs.count)   Not Required OU/CN Rules: $($Package.NotRequiredOUs.count)" -ForegroundColor $textcolour	
				
				if ($DetailedLoggingEnabled) {
					Add-Content -Path "$LogFile" -Value "`r`nActive Directory OU/CN Rules"
                    Add-Content -Path "$LogFile" -Value "----------------------------"
					$Package | select RequiredOrOUs, NotRequiredOUs | Sort RequiredOrOUs, NotRequiredOUs | ft * -AutoSize | Out-String -Width 4096 | Out-File -filepath "$LogFile" -append -Encoding Unicode
	
					Add-Content -Path "$LogFile" -Value "$($SimString)Included OU/CN Or Rules: $($Package.RequiredOrOUs.count)   Not Required OU/CN Rules: $($Package.NotRequiredOUs.count)"
				}
			}

			if ($GotGroupUsersOK -eq $true) {
				if ($GroupRulesOK -eq $true) {

					$PackageExists = @()
					$PackageExists = Get-CsPolicyPackage -Identity $Package.Package -WarningAction SilentlyContinue

					if ($PackageExists) {
						if ($UsersToProcess.count -gt 0) {
							$e = $error.count
	#						if ($UseLocalAttributeFiltering -eq $true -and $UseAzureADforLocalAttributeFilteringByDefault -eq $true) {
							if ($UseLocalAttributeFiltering -eq $true -and $Package.FilterSource -eq 'AzureAD') {
								if (!$AllCSOnlineUsers) {
									$AllCSOnlineUsers = @(Get-CSOnlineUser -ErrorAction SilentlyContinue -WarningAction SilentlyContinue)
								}
							}
							if (!$AllAzureADUsers) {
								$AllAzureADUsers = @(AzureADPreview\Get-AzureADUser -all $true -ErrorAction SilentlyContinue -WarningAction SilentlyContinue)
							}
							if ($Package.ActionFilter) {
								if ($Filter -ne $Package.ActionFilter -or $FilterSource -ne $Package.FilterSource) {
									$Filter = $Package.ActionFilter
									$FilterSource = $Package.FilterSource
									$FilterTypeSource = [string]$Package.FilterType + " " + [string]$Package.FilterSource
									if ($UseLocalAttributeFiltering -eq $false) {
										$OnlineUsers = @(Get-CSOnlineUser -Filter $Filter -ErrorAction SilentlyContinue -WarningAction SilentlyContinue)
										$ObjectField = 'Identity'
									} else {
										if ($FilterSource -eq 'AzureAD') {
											$OnlineUsers = @($AllAzureADUsers | where { Invoke-Expression $Filter })
											$ObjectField = 'ObjectID'
										} else {
											if (!$AllCSOnlineUsers) {
												$AllCSOnlineUsers = @(Get-CSOnlineUser -ErrorAction SilentlyContinue -WarningAction SilentlyContinue)
											}
											$OnlineUsers = @($AllCSOnlineUsers | where { Invoke-Expression $Filter })
											$ObjectField = 'Identity'
										}
									}
								}
							} else {
								if ($Filter) {
									$Filter = $null
									$FilterSource = $null
									$FilterTypeSource = '-'
									$OnlineUsers = @()
								}
							}
							
							if ($error.count -eq $e) {
								[int]$ResultCount=0
								[int]$UsersProcessed=0
								$funcstarttime = (Get-Date)
								foreach ($user in $UsersToProcess) {
									$UserIsLicensed = $User.IsLicensed
									if ($UserIsLicensed -eq $false -and $CurrentlyUnlicensedUsers.ObjectID -notcontains $User.ObjectID) {
										$UserIsLicensed = $true
									}
									if ($Package.SkipUpdatedLicensingCheck -eq $true -or $UserIsLicensed -eq $true) {	# 1.0.94

										if ($Package.HasGroupRules -eq $false -or ($GroupMatchedUsers.ObjectID -contains $User.ObjectID)) { # 1.0.25 hopefully improved processing time

											$onlineobj = $null
											if ($Package.ActionFilter) {
												$onlineobj = $OnlineUsers | where { $_.($ObjectField) -eq $User.ObjectID } | select -First 1 # 1.0.84
											}
											
											if (!$Filter -or $onlineobj) {
																						
		# msonline - Email address and ObjectID
		# csonline - UserPrincipalName and Identity
		# AzureAD - UserPrincipalName  and ObjectID
		
												$UserADObj = $null
												# both are required below
												if ($FilterSource -eq 'AzureAD' -and $UseLocalAttributeFiltering -eq $true) {
													# we already have the AD in the wrong place
													$ADObject = $null
													$ADObject = $onlineobj
												} else {
													# we already have CSOnline and need ADObject
													$ADObject = $null
													$ADObject = $AllAzureADUsers | where { $_.ObjectID -eq $User.ObjectID } | select -first 1
												}
												
												
												$UserPolicyContent = $null
												$ExistingPackage = $null
												$UserPolicyContent=Get-CsUserPolicyPackage -Identity $User.EmailAddress -WarningAction SilentlyContinue
												if ($UserPolicyContent) {
													$ExistingPackage=$UserPolicyContent.Name
												} else {
													$ExistingPackage = "(no result)"
												}	
		
												if ($Package.HasADOURules -eq $true) {
													$OU = $null
													$OU = ($AllAzureADUsers | where {$_.ObjectID -eq $User.ObjectID}).ExtensionProperty.onPremisesDistinguishedName
												}

												if ($Package.HasADOURules -eq $false -or ( (Check_ADCanonicalName -UserCN $OU -RequiredOrOUs $Package.RequiredOrOUs -NotRequiredOUs $Package.NotRequiredOUs) -eq $true)) {
													if (($ExistingPackage -ne $Package.Package -and $Package.ApplyOnlyIfExistingPackageBlank -eq $false) -or ($Package.ApplyOnlyIfExistingPackageBlank -eq $true -and ($ExistingPackage -eq '(no result)' -or $ExistingPackage -eq 'NONE'))) {
														if ($Package.ProcessDisabledUsers -eq $true -or $ADObject.AccountEnabled -eq $true) { # 1.0.61

															if ($Package.CollectionID -eq 0 -or ( @($UserOutput | where {$_.CollectionID -gt $Package.CollectionIDIgnoreThreshold -and $_.CollectionID -lt $Package.CollectionID -and $_.CollectionID -notin @($Package.CollectionIDsToExclude) -and $_.ObjectID -eq $User.ObjectID -and ($_.UserType -eq $Package.UserType -or $IgnoreCollectionIDUserType -eq $true) -and $_.State -gt 1 }).count -eq 0)) { #1.0.86
															

																$UsersToApply += $User # 1.0.26
																#user to be processed, add output here

																$UserOutput += [PSCustomObject]@{
																	'PackageType' = $Package.UserType
																	'Settings' = $Package.Settings
																	'Default' = $Package.Default
																	'CollectionID' = $Package.CollectionID
																	#'CollectionIDIgnoreThreshold' = $Package.CollectionIDIgnoreThreshold
																	'DisplayName' = $user.DisplayName
																	'EmailAddress' = $user.EmailAddress
																	'Status' = "Package Update Required"
																	'IsLicensed' = $UserIsLicensed
																	#'Enabled' = $onlineobj.Enabled # 1.0.61
																	'Enabled' = $ADObject.AccountEnabled 
																	'ExistingPackage' = $ExistingPackage
																	'RequiredPackage' = $Package.Package
																	'PackageDescription' = $Package.Description
																	#'PackageFilter' = $Package.ActionFilter
																	#'FilterTypeSource' = $FilterTypeSource
																	#'CSorADOnlineUsers' = $OnlineUsers.Count
																	#'HasGroupRules' = $Package.HasGroupRules
																	#'RequiredGroups' = $RequiredRecursiveIDs.count 
																	#'RequiredORGroups' = $RequiredORRecursiveIDs.count
																	#'NotRequiredGroups' = $NotRequiredRecursiveIDs.count
																	#'HasADOUCNRules' = $Package.HasADOURules
																	#'RequiredORADOUCNs' = $Package.RequiredOrOUs.count
																	#'NotRequiredADOUCNs' = $Package.NotRequiredOUs.count																	
																	'ApplyOnlyIfExistingPackageBlank' = $Package.ApplyOnlyIfExistingPackageBlank
																	'Simulation' = !$MakeChanges
																	'ObjectID' = $user.ObjectID
																	'State' = 2
																	'FailedUserToBeRemovedFromLicensingGroup' = $false
																}
															} else {
																$UserOutput += [PSCustomObject]@{
																	'PackageType' = $Package.UserType
																	'Settings' = $Package.Settings
																	'Default' = $Package.Default
																	'CollectionID' = $Package.CollectionID
																	#'CollectionIDIgnoreThreshold' = $Package.CollectionIDIgnoreThreshold
																	'DisplayName' = $user.DisplayName
																	'EmailAddress' = $user.EmailAddress
																	'Status' = "User filtered by CollectionID"
																	'IsLicensed' = $UserIsLicensed
																	#'Enabled' = $onlineobj.Enabled # 1.0.61
																	'Enabled' = $ADObject.AccountEnabled 
																	'ExistingPackage' = $ExistingPackage
																	'RequiredPackage' = $Package.Package
																	'PackageDescription' = $Package.Description
																	#'PackageFilter' = $Package.ActionFilter
																	#'FilterTypeSource' = $FilterTypeSource
																	#'CSorADOnlineUsers' = $OnlineUsers.Count
																	#'HasGroupRules' = $Package.HasGroupRules
																	#'RequiredGroups' = $RequiredRecursiveIDs.count 
																	#'RequiredORGroups' = $RequiredORRecursiveIDs.count
																	#'NotRequiredGroups' = $NotRequiredRecursiveIDs.count
																	#'HasADOUCNRules' = $Package.HasADOURules
																	#'RequiredORADOUCNs' = $Package.RequiredOrOUs.count
																	#'NotRequiredADOUCNs' = $Package.NotRequiredOUs.count																	
																	'ApplyOnlyIfExistingPackageBlank' = $Package.ApplyOnlyIfExistingPackageBlank
																	'Simulation' = !$MakeChanges
																	'ObjectID' = $user.ObjectID
																	'State' = -3
																	'FailedUserToBeRemovedFromLicensingGroup' = $false
																}

															}

														} else {
															$DisabledUsersThisPackage+=1
															$UserOutput += [PSCustomObject]@{
																'PackageType' = $Package.UserType
																'Settings' = $Package.Settings
																'Default' = $Package.Default
																'CollectionID' = $Package.CollectionID
																#'CollectionIDIgnoreThreshold' = $Package.CollectionIDIgnoreThreshold
																'DisplayName' = $user.DisplayName
																'EmailAddress' = $user.EmailAddress
																'Status' = "User Is Not Enabled"
																'IsLicensed' = $UserIsLicensed
																#'Enabled' = $onlineobj.Enabled # 1.0.61
																'Enabled' = $ADObject.AccountEnabled 
																'ExistingPackage' = $ExistingPackage
																'RequiredPackage' = $Package.Package
																'PackageDescription' = $Package.Description
																#'PackageFilter' = $Package.ActionFilter
																#'FilterTypeSource' = $FilterTypeSource
																#'CSorADOnlineUsers' = $OnlineUsers.Count
																#'HasGroupRules' = $Package.HasGroupRules
																#'RequiredGroups' = $RequiredRecursiveIDs.count 
																#'RequiredORGroups' = $RequiredORRecursiveIDs.count
																#'NotRequiredGroups' = $NotRequiredRecursiveIDs.count
																#'HasADOUCNRules' = $Package.HasADOURules
																#'RequiredORADOUCNs' = $Package.RequiredOrOUs.count
																#'NotRequiredADOUCNs' = $Package.NotRequiredOUs.count																
																'ApplyOnlyIfExistingPackageBlank' = $Package.ApplyOnlyIfExistingPackageBlank
																'Simulation' = !$MakeChanges
																'ObjectID' = $user.ObjectID
																'State' = -2
																'FailedUserToBeRemovedFromLicensingGroup' = $RemoveLGForDisabledUsers
															}

															if ( ($Package.OnFailureApplytoAllUsersOnNextRun -band 4) -eq 4 -or ($GlobalOnFailureApplytoAllUsersOnNextRun -band 2) -eq 2) {
																if ($Package.UserType -eq 'Teacher') {
																	$ApplytoAllTeachersOnNextRunThisFunc+=1
																} else {
																	$ApplytoAllStudentsOnNextRunThisFunc+=1	
																}
															}


															$DisabledUsersErrorsCaptured+=1
															if ($DisabledUsersErrorsCaptured -le $RepeatLogEventThreshold) {
																write-host "Cannot Apply Package to User - User is Disabled - CollectionID: $PadCollectionID   $PadSettings   Package Name: $PadPackage    User Email: $($user.EmailAddress)" -ForegroundColor Red
															}
			#												if ($DetailedLoggingEnabled) {
			#													Add-Content -Path "$LogFile" -Value "Cannot apply Package to user - user is Disabled - CollectionID: $PadCollectionID   $PadSettings   Package Name: $PadPackage    User Email: $($user.EmailAddress)" -Encoding Unicode
			#												}
															if ($DisabledUsersErrorsCaptured -eq $RepeatLogEventThreshold) {
																write-host "RepeatLogEventThereshold: Further messages about Disabled users will be supressed in Package Processing" -ForegroundColor yellow
															}

														}            

													} else {
														#user not to be processed due to package correct or not blank
														if ($Package.ApplyOnlyIfExistingPackageBlank -eq $true) {
															$string = 'User has the required Package or Existing Package is not Blank'
														} else {
															$string = 'User has the required Package'
														}
														$UserOutput += [PSCustomObject]@{
															'PackageType' = $Package.UserType
															'Settings' = $Package.Settings
															'Default' = $Package.Default
															'CollectionID' = $Package.CollectionID
															#'CollectionIDIgnoreThreshold' = $Package.CollectionIDIgnoreThreshold
															'DisplayName' = $user.DisplayName
															'EmailAddress' = $user.EmailAddress
															'Status' = $string
															'IsLicensed' = $UserIsLicensed
															#'Enabled' = $onlineobj.Enabled # 1.0.61
															'Enabled' = $ADObject.AccountEnabled 
															'ExistingPackage' = $ExistingPackage
															'RequiredPackage' = $Package.Package
															'PackageDescription' = $Package.Description
															#'PackageFilter' = $Package.ActionFilter
															#'FilterTypeSource' = $FilterTypeSource
															#'CSorADOnlineUsers' = $OnlineUsers.Count
															#'HasGroupRules' = $Package.HasGroupRules
															#'RequiredGroups' = $RequiredRecursiveIDs.count 
															#'RequiredORGroups' = $RequiredORRecursiveIDs.count
															#'NotRequiredGroups' = $NotRequiredRecursiveIDs.count
															#'HasADOUCNRules' = $Package.HasADOURules
															#'RequiredORADOUCNs' = $Package.RequiredOrOUs.count
															#'NotRequiredADOUCNs' = $Package.NotRequiredOUs.count															
															'ApplyOnlyIfExistingPackageBlank' = $Package.ApplyOnlyIfExistingPackageBlank
															'Simulation' = !$MakeChanges
															'ObjectID' = $user.ObjectID
															'State' = if ($ExistingPackage -eq $Package.Package) { 3 } else { 0 }
															'FailedUserToBeRemovedFromLicensingGroup' = $false
														}			
													}

												} else {
													$UserOutput += [PSCustomObject]@{
														'PackageType' = $Package.UserType
														'Settings' = $Package.Settings
														'Default' = $Package.Default
														'CollectionID' = $Package.CollectionID
														#'CollectionIDIgnoreThreshold' = $Package.CollectionIDIgnoreThreshold
														'DisplayName' = $user.DisplayName
														'EmailAddress' = $user.EmailAddress
														'Status' = "User Filtered by AD OU/CN Rules"
														'IsLicensed' = $UserIsLicensed
														#'Enabled' = $onlineobj.Enabled # 1.0.61
														'Enabled' = $ADObject.AccountEnabled 
														'ExistingPackage' = $ExistingPackage
														'RequiredPackage' = $Package.Package
														'PackageDescription' = $Package.Description
														#'PackageFilter' = $Package.ActionFilter
														#'FilterTypeSource' = $FilterTypeSource
														#'CSorADOnlineUsers' = $OnlineUsers.Count
														#'HasGroupRules' = $Package.HasGroupRules
														#'RequiredGroups' = $RequiredRecursiveIDs.count 
														#'RequiredORGroups' = $RequiredORRecursiveIDs.count
														#'NotRequiredGroups' = $NotRequiredRecursiveIDs.count
														#'HasADOUCNRules' = $Package.HasADOURules
														#'RequiredORADOUCNs' = $Package.RequiredOrOUs.count
														#'NotRequiredADOUCNs' = $Package.NotRequiredOUs.count														
														'ApplyOnlyIfExistingPackageBlank' = $Package.ApplyOnlyIfExistingPackageBlank
														'Simulation' = !$MakeChanges
														'ObjectID' = $user.ObjectID
														'State' = 0
														'FailedUserToBeRemovedFromLicensingGroup' = $false
													}
												}

											} else {
											#user not found online, even though some filtered users were - user likely was filtered out
										
												if ($Filter) {
													$string = "User Missing in Filtered $FilterType $FilterSource"
													$stringb = "(user filtered)"
												} else {
													$string = "User Missing in $FilterType $FilterSource"
													$stringb = "(user missing)"
												}
												$ADObject = $AllAzureADUsers | where { $_.ObjectID -eq $user.ObjectID } | select -first 1
										
												$UserOutput += [PSCustomObject]@{
													'PackageType' = $Package.UserType
													'Settings' = $Package.Settings
													'Default' = $Package.Default
													'CollectionID' = $Package.CollectionID
													#'CollectionIDIgnoreThreshold' = $Package.CollectionIDIgnoreThreshold
													'DisplayName' = $user.DisplayName
													'EmailAddress' = $user.EmailAddress
													'Status' = $string
													'IsLicensed' = $UserIsLicensed
													'Enabled' = $ADObject.AccountEnabled
													'ExistingPackage' = $stringb
													'RequiredPackage' = $Package.Package
													'PackageDescription' = $Package.Description
													#'PackageFilter' = $Package.ActionFilter
													#'FilterTypeSource' = $FilterTypeSource
													#'CSorADOnlineUsers' = $OnlineUsers.Count
													#'HasGroupRules' = $Package.HasGroupRules
													#'RequiredGroups' = $RequiredRecursiveIDs.count 
													#'RequiredORGroups' = $RequiredORRecursiveIDs.count
													#'NotRequiredGroups' = $NotRequiredRecursiveIDs.count
													#'HasADOUCNRules' = $Package.HasADOURules
													#'RequiredORADOUCNs' = $Package.RequiredOrOUs.count
													#'NotRequiredADOUCNs' = $Package.NotRequiredOUs.count													
													'ApplyOnlyIfExistingPackageBlank' = $Package.ApplyOnlyIfExistingPackageBlank
													'Simulation' = !$MakeChanges
													'ObjectID' = $user.ObjectID
													'State' = 0
													'FailedUserToBeRemovedFromLicensingGroup' = $false
												}
											}

										} else {
											$ADObject = $AllAzureADUsers | where { $_.ObjectID -eq $user.ObjectID } | select -first 1 # 1.0.94
											$UserOutput += [PSCustomObject]@{
												'PackageType' = $Package.UserType
												'Settings' = $Package.Settings
												'Default' = $Package.Default
												'CollectionID' = $Package.CollectionID
												#'CollectionIDIgnoreThreshold' = $Package.CollectionIDIgnoreThreshold
												'DisplayName' = $user.DisplayName
												'EmailAddress' = $user.EmailAddress
												'Status' = "User Filtered by Group Rules"
												'IsLicensed' = $UserIsLicensed
												'Enabled' = $ADObject.AccountEnabled # 1.0.94
												'ExistingPackage' = ''
												'RequiredPackage' = $Package.Package
												'PackageDescription' = $Package.Description
												#'PackageFilter' = $Package.ActionFilter
												#'FilterTypeSource' = $FilterTypeSource
												#'CSorADOnlineUsers' = $OnlineUsers.Count
												#'HasGroupRules' = $Package.HasGroupRules
												#'RequiredGroups' = $RequiredRecursiveIDs.count 
												#'RequiredORGroups' = $RequiredORRecursiveIDs.count
												#'NotRequiredGroups' = $NotRequiredRecursiveIDs.count
												#'HasADOUCNRules' = $Package.HasADOURules
												#'RequiredORADOUCNs' = $Package.RequiredOrOUs.count
												#'NotRequiredADOUCNs' = $Package.NotRequiredOUs.count												
												'ApplyOnlyIfExistingPackageBlank' = $Package.ApplyOnlyIfExistingPackageBlank
												'Simulation' = !$MakeChanges
												'ObjectID' = $user.ObjectID
												'State' = 0
												'FailedUserToBeRemovedFromLicensingGroup' = $false
											}										
										}
								
									} else {
										$DisabledUsersThisPackage+=1 # if they are not licensed, they cant be enabled
										$UserOutput += [PSCustomObject]@{
											'PackageType' = $Package.UserType
											'Settings' = $Package.Settings
											'Default' = $Package.Default
											'CollectionID' = $Package.CollectionID
											#'CollectionIDIgnoreThreshold' = $Package.CollectionIDIgnoreThreshold
											'DisplayName' = $user.DisplayName
											'EmailAddress' = $user.EmailAddress
											'Status' = "User is Unlicensed"
											'IsLicensed' = $UserIsLicensed
											'Enabled' = ''
											'ExistingPackage' = ''
											'RequiredPackage' = $Package.Package
											'PackageDescription' = $Package.Description
											#'PackageFilter' = $Package.ActionFilter
											#'FilterTypeSource' = $FilterTypeSource
											#'CSorADOnlineUsers' = $OnlineUsers.Count
											#'HasGroupRules' = $Package.HasGroupRules
											#'RequiredGroups' = $RequiredRecursiveIDs.count 
											#'RequiredORGroups' = $RequiredORRecursiveIDs.count
											#'NotRequiredGroups' = $NotRequiredRecursiveIDs.count
											#'HasADOUCNRules' = $Package.HasADOURules
											#'RequiredORADOUCNs' = $Package.RequiredOrOUs.count
											#'NotRequiredADOUCNs' = $Package.NotRequiredOUs.count											
											'ApplyOnlyIfExistingPackageBlank' = $Package.ApplyOnlyIfExistingPackageBlank
											'Simulation' = !$MakeChanges
											'ObjectID' = $user.ObjectID
											'State' = -2
											'FailedUserToBeRemovedFromLicensingGroup' = $RemoveLGForNotLicensedUsers
										}
								
										if ( ($Package.OnFailureApplytoAllUsersOnNextRun -band 8) -eq 8 -or ($GlobalOnFailureApplytoAllUsersOnNextRun -band 2) -eq 2) {
											if ($Package.UserType -eq 'Teacher') {
												$ApplytoAllTeachersOnNextRunThisFunc+=1
											} else {
												$ApplytoAllStudentsOnNextRunThisFunc+=1	
											}
										}
									}
								
							
									$ResultCount+=1
									$UsersProcessed+=1



									if ($ResultCount -eq $LongJobUpdateInterval) {
										[int]$ResultCount=0

										$UsersProcessedString, $timestring, $timeremainingseconds = get-timeelapsed -StartTime $funcstarttime -Processed $UsersProcessed -ToProcess ($UsersToProcess.Count)

										if ($timeremainingseconds -ge 10) {
											write-host "$($simstring)Get Existing User Package - CollectionID: $PadCollectionID   $PadSettings - Processed $UsersProcessedString of $($UsersToProcess.Count) users - ETR$timestring" -ForegroundColor Gray
											
											if ($DetailedLoggingEnabled) {
												Add-Content -Path "$LogFile" -Value "$($simstring)Get Existing User Package - CollectionID: $PadCollectionID   $PadSettings - Processed $UsersProcessedString of $($UsersToProcess.Count) users - ETR$timestring" -Encoding Unicode  
											}
										}
									}
							
								} # end for user loop

								# successful package invocation - although no users may still have been affected
						
								if ($UsersToApply.count -gt 0) {
									[string]$OperationID = ''
									[string]$OperationName = ''

									if ($Package.UserType -eq 'Teacher') {
										$TeamsPackageBatchProcessingThreshold = $TeacherTeamsPackageBatchProcessingThreshold
									}

									if ($Package.UserType -eq 'Student') {
										$TeamsPackageBatchProcessingThreshold = $StudentTeamsPackageBatchProcessingThreshold
									}
									if ($UsersToApply.count -ge $TeamsPackageBatchProcessingThreshold) {
										
										[int]$BatchSkipFront=0
										[int]$TrackerCount = 0
										$Portion = $null
										$Portion = @()
										do {
											if ($UsersToApply.count -le $MaximumBatchSize) {
													$Portion = @($UsersToApply)
											} else {
												if ($UsersToApply.count -gt 0) {
													$Portion = @($UsersToApply | Select -Last ($UsersToApply.count - $BatchSkipFront) | Select -First $MaximumBatchSize)
												} else {
													$Portion = $UsersToApply
												}
											}
											$TrackerCount++
											
											$PadUsersToApply=[string]$Portion.count
											$PadUsersToApply = $PadUsersToApply.PadRight(4," ")
											$string = $Portion[0].EmailAddress
											
											if ($MakeChanges -eq $true) {
												$OperationID = $null
												$OperationName = "$($Package.CollectionID) - $($Package.Settings) - $($Package.Package) - STEPER Profile $ProfileName - Batch $TrackerCount"
												$OperationID = New-CsBatchPolicyPackageAssignmentOperation -PackageName $Package.Package -Identity $Portion.ObjectID -WarningAction SilentlyContinue #-OperationName # 1.0.84
											
												# may not return operation id

												if ($error.count -gt $e) {
													$errorstring = ""
													if ($Error[0].Exception.Message) {
														$errorstring = $Error[0].Exception.Message
													} else {
														$errorstring = "(unknown)"
													}

													foreach ($person in $Portion) {
														$AssignmentErrors += [PSCustomObject]@{    
															'PackageType' = $Package.UserType											        
															'BatchJob' = $true
															'UserCount' = 1
															'DisplayName' = $person.DisplayName
															'EmailAddress' = $person.EmailAddress
															'IsLicensed' = $person.IsLicensed
															'ObjectID' = $person.ObjectID 
															'Filter' = $Package.ActionFilter
															'FilterTypeSource' = $FilterTypeSource
															'Description' = $Package.Description
															'RequiredPackage' = $Package.Package
															'SettingsSource' = $Package.Settings
															'ErrorInstance' = $false
															'Error' = $errorstring
															'FailedUserToBeRemovedFromLicensingGroup' = $RemoveLGForAssignmentErrors
														}
													}
													$AssignmentErrors += [PSCustomObject]@{
														'PackageType' = $Package.UserType
														'BatchJob' = $true
														'UserCount' = $Portion.Count
														'DisplayName' = $null
														'EmailAddress' = $null
														'IsLicensed' = $null
														'ObjectID' = $null  
														'Filter' = $Package.ActionFilter
														'FilterTypeSource' = $FilterTypeSource 
														'Description' = $Package.Description
														'RequiredPackage' = $Package.Package
														'SettingsSource' = $Package.Settings
														'ErrorInstance' = $true
														'Error' = "Job " + [STRING]$TrackerCount + ": " + $errorstring
														'FailedUserToBeRemovedFromLicensingGroup' = $false
													}
																									
																									
													if ( ($Package.OnFailureApplytoAllUsersOnNextRun -band 2) -eq 2 -or ($GlobalOnFailureApplytoAllUsersOnNextRun -band 2) -eq 2) {
														if ($Package.UserType -eq 'Teacher') {
															$ApplytoAllTeachersOnNextRunThisFunc+=$Portion.Count
														} else {
															$ApplytoAllStudentsOnNextRunThisFunc+=$Portion.Count
														}
													}
																									
													$PackageBatchesSubmitted+= [PSCustomObject]@{
														'PackageType' = $Package.UserType
														'ApplyToAll' = $Package.ApplyToAll
														'Description' = $Package.Description
														'RequiredPackage' = $Package.Package
														'SettingsSource' = $Package.Settings
														'Simulation' = !$MakeChanges
														'BatchNumber' = $TrackerCount
														'UsersInBatch' = $Portion.count
														'TotalUsersInAllBatches' = $UsersToApply.Count
														'OperationID' = '-' 
														'IncludingUser' = $string
														'Error' = $errorstring
													}
												
													write-host "Failed to Submit Batch Job $($TrackerCount) - CollectionID: $PadCollectionID   $PadSettings   Package Name: $PadPackage   User Qty: $PadUsersToApply   Error: $errorstring" -ForegroundColor Red
													if ($DetailedLoggingEnabled) {
														Add-Content -Path "$LogFile" -Value "Failed to Submit Batch Job $($TrackerCount) - CollectionID: $PadCollectionID   $PadSettings   Package Name: $PadPackage   User Qty: $PadUsersToApply   Error: $errorstring" -Encoding Unicode
													}
												} else {
													$PackageBatchesSubmitted+= [PSCustomObject]@{
														'PackageType' = $Package.UserType
														'ApplyToAll' = $Package.ApplyToAll
														'Description' = $Package.Description
														'RequiredPackage' = $Package.Package
														'SettingsSource' = $Package.Settings
														'Simulation' = !$MakeChanges
														'BatchNumber' = $TrackerCount
														'UsersInBatch' = $Portion.count
														'TotalUsersInAllBatches' = $UsersToApply.Count
														'OperationID' = $OperationID 
														'IncludingUser' = $string
														'Error' = '(no error)'
													}
													if ($Verbose -eq $true) {   
														write-host "Submitted Package Batch Job $TrackerCount - CollectionID: $PadCollectionID   $PadSettings   Package Name: $PadPackage   User Qty: $PadUsersToApply   BatchID: $OperationID   Including: $string" -ForegroundColor Cyan
													}
													if ($DetailedLoggingEnabled) {
														Add-Content -Path "$LogFile" -Value "Submitted Package Batch Job $TrackerCount - CollectionID: $PadCollectionID   $PadSettings   Package Name: $PadPackage   User Qty: $PadUsersToApply   BatchID: $OperationID   Including: $string" -Encoding Unicode
													}
												}
											} else {
												$PackageBatchesSubmitted+= [PSCustomObject]@{
													'PackageType' = $Package.UserType
													'ApplyToAll' = $Package.ApplyToAll
													'Description' = $Package.Description
													'RequiredPackage' = $Package.Package
													'SettingsSource' = $Package.Settings
													'Simulation' = !$MakeChanges
													'BatchNumber' = $TrackerCount
													'UsersInBatch' = $Portion.count
													'TotalUsersInAllBatches' = $UsersToApply.Count
													'OperationID' = '-'
													'IncludingUser' = $string
													'Error' = '(no error)'
												}
												if ($Verbose -eq $true) {   
													write-host "Simulation: Submitted Package Batch Job $TrackerCount - CollectionID: $PadCollectionID   $PadSettings   Package Name: $PadPackage   User Qty: $PadUsersToApply   BatchID: -      Including: $string" -ForegroundColor Cyan
												}
												if ($DetailedLoggingEnabled) {
													Add-Content -Path "$LogFile" -Value "Simulation: Submitted Package Batch Job $TrackerCount - CollectionID: $PadCollectionID   $PadSettings   Package Name: $PadPackage   User Qty: $PadUsersToApply   BatchID: -      Including: $string" -Encoding Unicode
												}
											}
											
											if ($Portion.count -gt 0) {
												$BatchSkipFront=$BatchSkipFront+$Portion.Count
											} else {
												$BatchSkipFront++
											}
										} while ( $BatchSkipFront -lt $UsersToApply.count -and $UsersToApply.Count -gt 0)

										if ($TrackerCount -gt 1) {
											write-host "Total Number of Batches: $TrackerCount   Total Number of Users: $($UsersToApply.count)" -ForegroundColor green	
											if ($DetailedLoggingEnabled) {
												Add-Content -Path "$LogFile" -Value "Total Number of Batches: $TrackerCount   Total Number of Users: $($UsersToApply.count)" -Encoding Unicode
											}									
										}
											
										# work done
										$Output += [PSCustomObject]@{
											'PackageType' = $Package.UserType
											'SettingsSource' = $Package.Settings
											'Default' = $Package.Default
											'CollectionID' = $Package.CollectionID
											#'CollectionIDIgnoreThreshold' = $Package.CollectionIDIgnoreThreshold
											#'CollectionIDsToExclude' = $Package.CollectionIDsToExclude
											'ApplyToAll' = $Package.ApplyToAll
											'SourceUsers' = $UsersToProcess.count
											'TeamsDisabledUsers' = ''
											'Filter' = $Package.ActionFilter
											'FilterTypeSource' = $FilterTypeSource
											'CSorADOnlineUsers' = $OnlineUsers.Count
											'HasGroupRules' = $Package.HasGroupRules
											'RequiredGroups' = $RequiredRecursiveIDs.count 
											'RequiredORGroups' = $RequiredORRecursiveIDs.count
											'NotRequiredGroups' = $NotRequiredRecursiveIDs.count
											'HasADOUCNRules' = $Package.HasADOURules
											'RequiredORADOUCNs' = $Package.RequiredOrOUs.count
											'NotRequiredADOUCNs' = $Package.NotRequiredOUs.count											
											'ApplyOnlyIfExistingPackageBlank' = $Package.ApplyOnlyIfExistingPackageBlank
											'OnlyApplytoUnlicensedUsers' = $Package.OnlyApplyToUnlicensed
											'UsersModified' = $UsersToApply.Count
											'Description' = $Package.Description
											'RequiredPackage' = $Package.Package
											'Simulation' = !$MakeChanges
											'Status' = "Package Applied via " + [string]$TrackerCount + " Batches"
											'BatchID' = if ($TrackerCount -gt 1 -and $OperationID) { "Last: " + $OperationID } else { $OperationID }
											#'OperationName' = $OperationName
											'OperationName' = '-'
											'AssignmentErrors'=''
										}

									} else {
										$PadUsersToApply=[string]$UsersToApply.count
										$PadUsersToApply = $PadUsersToApply.PadRight(4," ")
										$string = $UsersToApply[0].EmailAddress
										if ($MakeChanges -eq $true) {

											if ($Verbose -eq $true) {   
												write-host "Applying User Package - CollectionID: $PadCollectionID   $PadSettings   Package Name: $PadPackage   User Qty: $PadUsersToApply   Including: $string" -ForegroundColor Yellow
											}
											if ($DetailedLoggingEnabled) {
												Add-Content -Path "$LogFile" -Value "Applying User Package - CollectionID: $PadCollectionID   $PadSettings   Package Name: $PadPackage   User Qty: $PadUsersToApply   Including: $string" -Encoding Unicode
											}

											[int]$ResultCount=0
											[int]$personprocessed=0
											$funcstarttime = (Get-Date)
											foreach ($person in $UsersToApply) {
												$ResultCount = $ResultCount + 1
												$personprocessed = $personprocessed + 1

												$e = $error.count
												Grant-CsUserPolicyPackage -PackageName $Package.Package -Identity $person.ObjectID -WarningAction SilentlyContinue -ErrorAction SilentlyContinue | out-null # 1.0.84
												if ($error.count -gt $e) {
													$errorstring = ""
													if ($Error[0].Exception.Message) {
														$errorstring = $Error[0].Exception.Message
													} else {
														$errorstring = "(unknown)"
													}
													$AssignmentErrors += [PSCustomObject]@{
														'PackageType' = $Package.UserType											        
														'BatchJob' = $false
														'UserCount' = 1
														'DisplayName' = $person.DisplayName
														'EmailAddress' = $person.EmailAddress
														'IsLicensed' = $person.IsLicensed
														'ObjectID' = $person.ObjectID 
														'Filter' = $Package.ActionFilter
														'FilterTypeSource' = $FilterTypeSource
														'Description' = $Package.Description
														'RequiredPackage' = $Package.Package
														'SettingsSource' = $Package.Settings
														'ErrorInstance' = $true
														'Error' = $errorstring
														'FailedUserToBeRemovedFromLicensingGroup' = $RemoveLGForAssignmentErrors
													}
													
													
													if ( ($Package.OnFailureApplytoAllUsersOnNextRun -band 2) -eq 2 -or ($GlobalOnFailureApplytoAllUsersOnNextRun -band 2) -eq 2) {
														if ($Package.UserType -eq 'Teacher') {
															$ApplytoAllTeachersOnNextRunThisFunc+=1
														} else {
															$ApplytoAllStudentsOnNextRunThisFunc+=1		
														}
													}
													
													
													$AssignmentErrorsThisPackage+=1
													$AssignmentErrorsCaptured+=1
													if ($AssignmentErrorsCaptured -le $RepeatLogEventThreshold) {
														write-host "Failed to apply Package to user - CollectionID: $PadCollectionID   $PadSettings   Package Name: $PadPackage   User Email: $($person.EmailAddress)   Error: $errorstring" -ForegroundColor Red
													}
													if ($AssignmentErrorsCaptured -eq $RepeatLogEventThreshold) {
														write-host "RepeatLogEventThereshold: Further direct Package Application errors will be supressed" -ForegroundColor yellow
													}
													if ($DetailedLoggingEnabled) {
														Add-Content -Path "$LogFile" -Value "Failed to apply Package to user - CollectionID: $PadCollectionID   $PadSettings   Package Name: $PadPackage   User Email: $($person.EmailAddress)   Error: $errorstring" -Encoding Unicode
													}
												
												}
												if ($ResultCount -eq $LongJobUpdateInterval) {
													[int]$ResultCount=0
													
													$UsersProcessedString, $timestring, $timeremainingseconds = get-timeelapsed -StartTime $funcstarttime -Processed $personprocessed -ToProcess ($UsersToApply.Count)
													
													if ($timeremainingseconds -ge 10) {

														write-host "$($simstring)Applying User Package - CollectionID: $PadCollectionID   $PadSettings   Package Name: $PadPackage   Processed $UsersProcessedString of $($UsersToApply.Count) users - ETR$timestring" -ForegroundColor Gray

														if ($DetailedLoggingEnabled) {
															Add-Content -Path "$LogFile" -Value "$($simstring)Applying User Package -  CollectionID: $PadCollectionID   $PadSettings   Package Name: $PadPackage   Processed $UsersProcessedString of $($UsersToApply.Count) users - ETR$timestring" -Encoding Unicode  
														}
													}
												}
											}

										} else {
											if ($Verbose -eq $true) {   
												write-host "$($simstring)Applying User Package - CollectionID: $PadCollectionID   $PadSettings   Package Name: $PadPackage   User Qty: $PadUsersToApply   Including: $string" -ForegroundColor Cyan
											}
											if ($DetailedLoggingEnabled) {
												Add-Content -Path "$LogFile" -Value "$($simstring)Applying User Package - CollectionID: $PadCollectionID   $PadSettings   Package Name: $PadPackage   User Qty: $PadUsersToApply   Including: $string" -Encoding Unicode
											}
										}
										# work done
										$Output += [PSCustomObject]@{
											'PackageType' = $Package.UserType
											'SettingsSource' = $Package.Settings
											'Default' = $Package.Default
											'CollectionID' = $Package.CollectionID
											#'CollectionIDIgnoreThreshold' = $Package.CollectionIDIgnoreThreshold
											#'CollectionIDsToExclude' = $Package.CollectionIDsToExclude
											'ApplyToAll' = $Package.ApplyToAll
											'SourceUsers' = $UsersToProcess.count
											'TeamsDisabledUsers' = $DisabledUsersThisPackage
											'Filter' = $Package.ActionFilter
											'FilterTypeSource' = $FilterTypeSource
											'CSorADOnlineUsers' = $OnlineUsers.Count
											'HasGroupRules' = $Package.HasGroupRules
											'RequiredGroups' = $RequiredRecursiveIDs.count 
											'RequiredORGroups' = $RequiredORRecursiveIDs.count
											'NotRequiredGroups' = $NotRequiredRecursiveIDs.count
											'HasADOUCNRules' = $Package.HasADOURules
											'RequiredORADOUCNs' = $Package.RequiredOrOUs.count
											'NotRequiredADOUCNs' = $Package.NotRequiredOUs.count
											'ApplyOnlyIfExistingPackageBlank' = $Package.ApplyOnlyIfExistingPackageBlank
											'OnlyApplytoUnlicensedUsers' = $Package.OnlyApplyToUnlicensed
											'UsersModified' = $UsersToApply.Count
											'Description' = $Package.Description
											'RequiredPackage' = $Package.Package
											'Simulation' = !$MakeChanges
											'Status' = "Package Applied Directly"
											'BatchID' = '-'
											'OperationName' = '-'
											'AssignmentErrors'= $AssignmentErrorsThisPackage
										}
									}
								} else {
									# no work to do
									if ($Verbose -eq $true) {   
										write-host "$($simstring)No Package Changes Required - CollectionID: $PadCollectionID   $PadSettings   Package Name: $PadPackage" -ForegroundColor $textcolour
									}
									if ($DetailedLoggingEnabled) {
										Add-Content -Path "$LogFile" -Value "$($simstring)No Package Changes Required - CollectionID: $PadCollectionID   $PadSettings   Package Name: $PadPackage" -Encoding Unicode
									}
																	
									
									$Output += [PSCustomObject]@{
										'PackageType' = $Package.UserType
										'SettingsSource' = $Package.Settings
										'Default' = $Package.Default
										'CollectionID' = $Package.CollectionID
										#'CollectionIDIgnoreThreshold' = $Package.CollectionIDIgnoreThreshold
										#'CollectionIDsToExclude' = $Package.CollectionIDsToExclude
										'ApplyToAll' = $Package.ApplyToAll
										'SourceUsers' = $UsersToProcess.count
										'TeamsDisabledUsers' = $DisabledUsersThisPackage
										'Filter' = $Package.ActionFilter
										'FilterTypeSource' = $FilterTypeSource
										'CSorADOnlineUsers' = $OnlineUsers.Count
										'HasGroupRules' = $Package.HasGroupRules
										'RequiredGroups' = $RequiredRecursiveIDs.count 
										'RequiredORGroups' = $RequiredORRecursiveIDs.count
										'NotRequiredGroups' = $NotRequiredRecursiveIDs.count
										'HasADOUCNRules' = $Package.HasADOURules
										'RequiredORADOUCNs' = $Package.RequiredOrOUs.count
										'NotRequiredADOUCNs' = $Package.NotRequiredOUs.count
										'ApplyOnlyIfExistingPackageBlank' = $Package.ApplyOnlyIfExistingPackageBlank
										'OnlyApplytoUnlicensedUsers' = $Package.OnlyApplyToUnlicensed
										'UsersModified' = $UsersToApply.Count
										'Description' = $Package.Description
										'RequiredPackage' = $Package.Package
										'Simulation' = !$MakeChanges
										'Status' = "No Package Changes Required"
										'BatchID' = '-'
										'OperationName' = '-'
										'AssignmentErrors'= $AssignmentErrorsThisPackage
									}
								}
							} else {
								# no online users
								if ($Filter) {
									$string = "No filtered $FilterType $FilterSource users"
								} else {
									$string = "No $FilterType $FilterSource users"
								}
								$errorstring = ""
								if ($error.count -gt $e) {
									if ($Error[0].Exception.Message) {
										$errorstring =  " - error message: " + $Error[0].Exception.Message
									}
									$Filter = $null
									$OnlineUsers = @()

									$PadUsersNotProcessed=[string]$UsersToProcess.count
									$PadUsersNotProcessed = $PadUsersNotProcessed.PadRight(4," ")

									write-host "There was an error in retrieving $FilterType $FilterSource Users - CollectionID: $PadCollectionID   $PadSettings   Package Name: $PadPackage   Users Not Processed: $PadUsersNotProcessed" -ForegroundColor Red
									$warnings += "- There was an error in retrieving $FilterType $FilterSource Users - CollectionID: $PadCollectionID   $PadSettings   Package Name: $PadPackage   Users Not Processed: $PadUsersNotProcessed"
									if ($DetailedLoggingEnabled) {
										Add-Content -Path "$LogFile" -Value "There was an error in retrieving $FilterType $FilterSource Users - CollectionID: $PadCollectionID   $PadSettings   Package Name: $PadPackage   Users Not Processed: $PadUsersNotProcessed" -Encoding Unicode
									}
								}


								$Output += [PSCustomObject]@{
									'PackageType' = $Package.UserType
									'SettingsSource' = $Package.Settings
									'Default' = $Package.Default
									'CollectionID' = $Package.CollectionID
									#'CollectionIDIgnoreThreshold' = $Package.CollectionIDIgnoreThreshold
									#'CollectionIDsToExclude' = $Package.CollectionIDsToExclude
									'ApplyToAll' = $Package.ApplyToAll
									'SourceUsers' = $UsersToProcess.count
									'TeamsDisabledUsers' = ''
									'Filter' = $Package.ActionFilter
									'FilterTypeSource' = $FilterTypeSource
									'CSorADOnlineUsers' = 0
									'HasGroupRules' = $Package.HasGroupRules
									'RequiredGroups' = $RequiredRecursiveIDs.count 
									'RequiredORGroups' = $RequiredORRecursiveIDs.count
									'NotRequiredGroups' = $NotRequiredRecursiveIDs.count
									'HasADOUCNRules' = $Package.HasADOURules
									'RequiredORADOUCNs' = $Package.RequiredOrOUs.count
									'NotRequiredADOUCNs' = $Package.NotRequiredOUs.count
									'ApplyOnlyIfExistingPackageBlank' = $Package.ApplyOnlyIfExistingPackageBlank
									'OnlyApplytoUnlicensedUsers' = $Package.OnlyApplyToUnlicensed
									'UsersModified' = 0
									'Description' = $Package.Description
									'RequiredPackage' = $Package.Package
									'Simulation' = !$MakeChanges
									'Status' = $string + $errorstring
									'BatchID' = '-'
									'OperationName' = '-'
									'AssignmentErrors'=''
								}
							}
						} else {
							#no source users for processing against
							$Output += [PSCustomObject]@{
								'PackageType' = $Package.UserType
								'SettingsSource' = $Package.Settings
								'Default' = $Package.Default
								'CollectionID' = $Package.CollectionID
								#'CollectionIDIgnoreThreshold' = $Package.CollectionIDIgnoreThreshold
								#'CollectionIDsToExclude' = $Package.CollectionIDsToExclude
								'ApplyToAll' = $Package.ApplyToAll
								'SourceUsers' = $UsersToProcess.count
								'TeamsDisabledUsers' = ''
								'Filter' = $Package.ActionFilter
								'FilterTypeSource' = $FilterTypeSource 
								'CSorADOnlineUsers' = ''
								'HasGroupRules' = $Package.HasGroupRules
								'RequiredGroups' = $RequiredRecursiveIDs.count 
								'RequiredORGroups' = $RequiredORRecursiveIDs.count
								'NotRequiredGroups' = $NotRequiredRecursiveIDs.count
								'HasADOUCNRules' = $Package.HasADOURules
								'RequiredORADOUCNs' = $Package.RequiredOrOUs.count
								'NotRequiredADOUCNs' = $Package.NotRequiredOUs.count
								'ApplyOnlyIfExistingPackageBlank' = $Package.ApplyOnlyIfExistingPackageBlank
								'OnlyApplytoUnlicensedUsers' = $Package.OnlyApplyToUnlicensed
								'UsersModified' = 0
								'Description' = $Package.Description
								'RequiredPackage' = $Package.Package
								'Simulation' = !$MakeChanges
								'Status' = "No Source Users to Process"
								'BatchID' = '-'
								'OperationName' = '-'
								'AssignmentErrors'=''
							}
						}
					} else {
					# package not found
						$Output += [PSCustomObject]@{
							'PackageType' = $Package.UserType
							'SettingsSource' = $Package.Settings
							'Default' = $Package.Default
							'CollectionID' = $Package.CollectionID
							#'CollectionIDIgnoreThreshold' = $Package.CollectionIDIgnoreThreshold
							#'CollectionIDsToExclude' = $Package.CollectionIDsToExclude
							'ApplyToAll' = $Package.ApplyToAll
							'SourceUsers' = $UsersToProcess.count
							'TeamsDisabledUsers' = ''
							'Filter' = $Package.ActionFilter
							'FilterTypeSource' = $FilterTypeSource
							'CSorADOnlineUsers' = ''
							'HasGroupRules' = $Package.HasGroupRules
							'RequiredGroups' = $RequiredRecursiveIDs.count 
							'RequiredORGroups' = $RequiredORRecursiveIDs.count
							'NotRequiredGroups' = $NotRequiredRecursiveIDs.count
							'HasADOUCNRules' = $Package.HasADOURules
							'RequiredORADOUCNs' = $Package.RequiredOrOUs.count
							'NotRequiredADOUCNs' = $Package.NotRequiredOUs.count
							'ApplyOnlyIfExistingPackageBlank' = $Package.ApplyOnlyIfExistingPackageBlank
							'OnlyApplytoUnlicensedUsers' = $Package.OnlyApplyToUnlicensed
							'UsersModified' = 0
							'Description' = $Package.Description
							'RequiredPackage' = $Package.Package
							'Simulation' = !$MakeChanges
							'Status' = "Package Not Found"
							'BatchID' = '-'
							'OperationName' = '-'
							'AssignmentErrors'=''
						}

						$PadUsersNotProcessed=[string]$UsersToProcess.count
						$PadUsersNotProcessed = $PadUsersNotProcessed.PadRight(4," ")

						write-host "Package Not Found - CollectionID: $PadCollectionID   $PadSettings   Package Name: $PadPackage   Users Not Processed: $PadUsersNotProcessed" -ForegroundColor Yellow
						$warnings += "- Package Not Found - CollectionID: $PadCollectionID   $PadSettings   Package Name: $PadPackage   Users Not Processed: $PadUsersNotProcessed"
						if ($DetailedLoggingEnabled) {
							Add-Content -Path "$LogFile" -Value "Package Not Found - CollectionID: $PadCollectionID   $PadSettings   Package Name: $PadPackage   Users Not Processed: $PadUsersNotProcessed" -Encoding Unicode
						}
					}
				} else {
				# group filter means no users can be processed
					$Output += [PSCustomObject]@{
						'PackageType' = $Package.UserType
						'SettingsSource' = $Package.Settings
						'Default' = $Package.Default
						'CollectionID' = $Package.CollectionID
						#'CollectionIDIgnoreThreshold' = $Package.CollectionIDIgnoreThreshold
						#'CollectionIDsToExclude' = $Package.CollectionIDsToExclude
						'ApplyToAll' = $Package.ApplyToAll
						'SourceUsers' = $UsersToProcess.count
						'TeamsDisabledUsers' = ''
						'Filter' = $Package.ActionFilter
						'FilterTypeSource' = $FilterTypeSourcee 
						'CSorADOnlineUsers' = ''
						'HasGroupRules' = $Package.HasGroupRules
						'RequiredGroups' = $RequiredRecursiveIDs.count 
						'RequiredORGroups' = $RequiredORRecursiveIDs.count
						'NotRequiredGroups' = $NotRequiredRecursiveIDs.count
						'HasADOUCNRules' = $Package.HasADOURules
						'RequiredORADOUCNs' = $Package.RequiredOrOUs.count
						'NotRequiredADOUCNs' = $Package.NotRequiredOUs.count
						'ApplyOnlyIfExistingPackageBlank' = $Package.ApplyOnlyIfExistingPackageBlank
						'OnlyApplytoUnlicensedUsers' = $Package.OnlyApplyToUnlicensed
						'UsersModified' = 0
						'Description' = $Package.Description
						'RequiredPackage' = $Package.Package
						'Simulation' = !$MakeChanges
						'Status' = "Group Rules Impossible"
						'BatchID' = '-'
						'OperationName' = '-'
						'AssignmentErrors'=''
					}

					$PadUsersNotProcessed=[string]$UsersToProcess.count
					$PadUsersNotProcessed = $PadUsersNotProcessed.PadRight(4," ")

					write-host "Group Rules Impossible - CollectionID: $PadCollectionID   $PadSettings   Package Name: $PadPackage   Users Not Processed: $PadUsersNotProcessed" -ForegroundColor Yellow
					$warnings += "- Group Rules Impossible - CollectionID: $PadCollectionID   $PadSettings   Package Name: $PadPackage   Users Not Processed: $PadUsersNotProcessed"
					if ($DetailedLoggingEnabled) {
						Add-Content -Path "$LogFile" -Value "Group Rules Impossible or Processing Error - CollectionID: $PadCollectionID   $PadSettings   Package Name: $PadPackage   Users Not Processed: $PadUsersNotProcessed" -Encoding Unicode
					}
				}
			
			} else {
			# group filter means no users can be processed
				$Output += [PSCustomObject]@{
					'PackageType' = $Package.UserType
					'SettingsSource' = $Package.Settings
					'Default' = $Package.Default
					'CollectionID' = $Package.CollectionID
					#'CollectionIDIgnoreThreshold' = $Package.CollectionIDIgnoreThreshold
					#'CollectionIDsToExclude' = $Package.CollectionIDsToExclude
					'ApplyToAll' = $Package.ApplyToAll
					'SourceUsers' = $UsersToProcess.count
					'TeamsDisabledUsers' = ''
					'Filter' = $Package.ActionFilter
					'FilterTypeSource' = $FilterTypeSourcee 
					'CSorADOnlineUsers' = ''
					'HasGroupRules' = $Package.HasGroupRules
					'RequiredGroups' = $RequiredRecursiveIDs.count 
					'RequiredORGroups' = $RequiredORRecursiveIDs.count
					'NotRequiredGroups' = $NotRequiredRecursiveIDs.count
					'HasADOUCNRules' = $Package.HasADOURules
					'RequiredORADOUCNs' = $Package.RequiredOrOUs.count
					'NotRequiredADOUCNs' = $Package.NotRequiredOUs.count
					'ApplyOnlyIfExistingPackageBlank' = $Package.ApplyOnlyIfExistingPackageBlank
					'OnlyApplytoUnlicensedUsers' = $Package.OnlyApplyToUnlicensed
					'UsersModified' = 0
					'Description' = $Package.Description
					'RequiredPackage' = $Package.Package
					'Simulation' = !$MakeChanges
					'Status' = "Error retrieving Group Rules Users"
					'BatchID' = '-'
					'OperationName' = '-'
					'AssignmentErrors'=''
				}

				$PadUsersNotProcessed=[string]$UsersToProcess.count
				$PadUsersNotProcessed = $PadUsersNotProcessed.PadRight(4," ")

				write-host "Error retrieving Group Rules Users: CollectionID: $PadCollectionID   $PadSettings   Package Name: $PadPackage   Users Not Processed: $PadUsersNotProcessed" -ForegroundColor Yellow
				$warnings += "- Group Rules Impossible or Processing Error: CollectionID: $PadCollectionID   $PadSettings   Package Name: $PadPackage   Users Not Processed: $PadUsersNotProcessed"
				if ($DetailedLoggingEnabled) {
					Add-Content -Path "$LogFile" -Value "Error retrieving Group Rules Users: CollectionID: $PadCollectionID   $PadSettings   Package Name: $PadPackage   Users Not Processed: $PadUsersNotProcessed" -Encoding Unicode
				}
			}			

        } #end for package loop      

        $TeacherPoliciesProcessed = @($Output | where { $_.PackageType -eq 'Teacher' })
        $TeacherQty = ($TeacherPoliciesProcessed.usersmodified | Measure-Object -sum).sum
        $TeacherPolicies = @($PackagesToProcess | where {$_.UserType -eq 'Teacher' }).count

        $StudentPoliciesProcessed = @($Output | where { $_.PackageType -eq 'Student' })
        $StudentQty = ($StudentPoliciesProcessed.usersmodified | Measure-Object -sum).sum
        $StudentPolicies = @($PackagesToProcess | where {$_.UserType -eq 'Student' }).count

        if ($DetailedLoggingEnabled) {
            Add-Content -Path "$LogFile" -Value "`r`nUser Package Change Results:" -Encoding Unicode
            Add-Content -Path "$LogFile" -Value "----------------------------" -Encoding Unicode
        
            Process-UserCSVOutput ($UserOutput)
            $littleobj = @()
			$littleobj = @($UserOutput | where { $_.Status -eq 'Package Update Required' })
            Add-Content -Path "$LogFile" -Value "$($SimString)$($UserOutput.count) User Package results with $($littleobj.count) Total Package Changes" -Encoding Unicode
            $littleobj = @($littleobj  | Sort-Object -Property ObjectID -Unique) # 1.0.94
            Add-Content -Path "$LogFile" -Value "$($littleobj.count) Unique Users that required Package Changes`r`n" -Encoding Unicode

			$DefaultUserOutput  = @($UserOutput | where { $_.Default -eq $true})
			$littleobj = @($DefaultUserOutput | where { $_.Status -eq 'Package Update Required' })
            Add-Content -Path "$LogFile" -Value "$($SimString)$($DefaultUserOutput.count) User Package results with $($littleobj.count) Total Package Changes under Default Processing" -Encoding Unicode
            $littleobj = @($littleobj  | Sort-Object -Property ObjectID -Unique) # 1.0.94
            Add-Content -Path "$LogFile" -Value "$($littleobj.count) Unique Users that required Package Changes under Default Processing`r`n" -Encoding Unicode

        }

		$NotEnabled = @($UserOutput | where {  $_.Enabled -ne $true -and $_.IsLicensed -eq $true -and ($_.State -ne 0 -and $_.State -ne 1 ) }) # 1.0.94
		
        $NotEnabledUser = @($NotEnabled | Sort-Object -Property ObjectID -Unique) # 1.0.94
        if ($DetailedLoggingEnabled) {
            Add-Content -Path "$LogFile" -Value "`r`nLicensed Users That Are Not Enabled:" -Encoding Unicode
			Add-Content -Path "$LogFile" -Value "------------------------------------" -Encoding Unicode
            if ($NotEnabled.Count -gt 0) {
                #Process-UserCSVOutput ($NotEnabledUser | Select -property * -exclude ExistingPackage, RequiredPackage, PackageFilter, PackageDescription, ApplyOnlyIfExistingPackageBlank, Settings, Status, FailedUserToBeRemovedFromLicensingGroup | Sort PackageType, EmailAddress)
				Process-UserCSVOutput ($NotEnabledUser | where {$true} -Pipelinevariable UserC | Select -Property *, @{name="PackageInstances"; expression={ @($NotEnabled | where {$_.ObjectID -eq $UserC.ObjectID}).count }} | Select -property * -exclude Default, CollectionID, ExistingPackage, RequiredPackage, PackageFilter, FilterTypeSource, CSorADOnlineUsers, HasGroupRules, RequiredGroups, RequiredORGroups, NotRequiredGroups, HasADOUCNRules, RequiredORADOUCNs, NotRequiredADOUCNs, PackageDescription, ApplyOnlyIfExistingPackageBlank, Settings, Status, State, FailedUserToBeRemovedFromLicensingGroup | Sort @{Expression = "PackageType"; Descending = $true}, EmailAddress, FailedUserToBeRemovedFromLicensingGroup) # 1.0.94
                Add-Content -Path "$LogFile" -Value "$($NotEnabled.Count) Licensed Users That Are Not Enabled Error Instances were found during Package Processing, with $($NotEnabledUser.Count) users affected`r`n" -Encoding Unicode
            } else {
                Add-Content -Path "$LogFile" -Value "(none)`r`n" -Encoding Unicode
            }
        }
        if ($NotEnabled.Count -gt 0) {
            write-host "$($NotEnabled.Count) Licensed Users That Are Not Enabled Error Instances were found during Package Processing, with $($NotEnabledUser.Count) users affected" -ForegroundColor Red
	        $warnings += "- $($NotEnabled.Count) Licensed Users That Are Not Enabled Error Instances were found during Package Processing, with $($NotEnabledUser.Count) users affected"

            $IssueSummary += [PSCustomObject]@{
                'IssueType' = 'Licensed Users That Are Not Enabled - Instances'
                'Teacher' =   @($NotEnabled | where { $_.PackageType -eq 'Teacher'}).count
                'Student' =  @($NotEnabled | where { $_.PackageType -eq 'Student'}).count
                'Total' = $NotEnabled.Count
            }
            $IssueSummary += [PSCustomObject]@{
                'IssueType' = 'Licensed Users That Are Not Enabled - Users'
                'Teacher' =   @($NotEnabledUser | where { $_.PackageType -eq 'Teacher'}).count
                'Student' =  @($NotEnabledUser | where { $_.PackageType -eq 'Student'}).count
                'Total' = $NotEnabledUser.Count
            }
        }

        $NotLicensed = @($UserOutput | where { $_.IsLicensed -ne $true}) # 1.0.94
        $NotLicensedUser = @($NotLicensed | Sort-Object -Property ObjectID -Unique) # 1.0.94
        if ($DetailedLoggingEnabled) {
            Add-Content -Path "$LogFile" -Value "`r`nUnlicensed Users:" -Encoding Unicode
			Add-Content -Path "$LogFile" -Value "-----------------" -Encoding Unicode
            if ($NotLicensedUser.Count -gt 0) {
                #Process-UserCSVOutput ($NotLicensedUser | Select -property * -exclude ExistingPackage, RequiredPackage, PackageFilter, PackageDescription, ApplyOnlyIfExistingPackageBlank, Settings, Status, FailedUserToBeRemovedFromLicensingGroup | Sort PackageType, EmailAddress)
				Process-UserCSVOutput ($NotLicensedUser | where {$true} -Pipelinevariable UserC | Select -Property *, @{name="PackageInstances"; expression={ @($NotLicensed | where {$_.ObjectID -eq $UserC.ObjectID}).count }} | Select -property * -exclude Default, CollectionID, ExistingPackage, RequiredPackage, PackageFilter, FilterTypeSource, CSorADOnlineUsers, HasGroupRules, RequiredGroups, RequiredORGroups, NotRequiredGroups, HasADOUCNRules, RequiredORADOUCNs, NotRequiredADOUCNs, PackageDescription, ApplyOnlyIfExistingPackageBlank, Settings, Status, State, FailedUserToBeRemovedFromLicensingGroup  | Sort @{Expression = "PackageType"; Descending = $true}, EmailAddress, FailedUserToBeRemovedFromLicensingGroup) #1.0.94
                Add-Content -Path "$LogFile" -Value "$($NotLicensed.count) Unlicensed User Error Instances were encountered during Package Processing, with $($NotLicensedUser.count) users affected`r`n" -Encoding Unicode
            } else {
                Add-Content -Path "$LogFile" -Value "(none)`r`n" -Encoding Unicode
            }
        }
        if ($NotLicensed.Count -gt 0) {
            write-host "$($NotLicensed.count) Unlicensed User Error Instances were encountered during Package Processing, with $($NotLicensedUser.count) users affected" -ForegroundColor Red
	        $warnings += "- $($NotLicensed.count) Unlicensed User Error Instances were encountered during Package Processing, with $($NotLicensedUser.count) users affected"

            $IssueSummary += [PSCustomObject]@{
                'IssueType' = 'Unlicensed Users - Instances'
                'Teacher' =   @($NotLicensed | where { $_.PackageType -eq 'Teacher'}).count
                'Student' =  @($NotLicensed | where { $_.PackageType -eq 'Student'}).count
                'Total' = $NotLicensed.Count
            }
            $IssueSummary += [PSCustomObject]@{
                'IssueType' = 'Unlicensed Users - Users'
                'Teacher' =   @($NotLicensedUser | where { $_.PackageType -eq 'Teacher'}).count
                'Student' =  @($NotLicensedUser | where { $_.PackageType -eq 'Student'}).count
                'Total' = $NotLicensedUser.Count
            }
        }

		$ApplyErrorUser = @($AssignmentErrors | Sort-Object -Property ObjectID -Unique) # 1.0.94
        $AssignmentErrors = @($AssignmentErrors | where { $_.ErrorInstance -eq $true})
        if ($DetailedLoggingEnabled) {
	        Add-Content -Path "$LogFile" -Value "`r`nApply Package Error Instances:" -Encoding Unicode
	        Add-Content -Path "$LogFile" -Value "------------------------------" -Encoding Unicode
            if ( @($AssignmentErrors).count -gt 0) {
                Process-UserCSVOutput ($AssignmentErrors | Sort EmailAddress, SettingsSource, PackageType, RequiredPackage)
                Add-Content -Path "$LogFile" -Value "$($AssignmentErrors.count) Apply Package Error Instances were encountered during Package Processing, with $($ApplyErrorUser.count) users affected`r`n" -Encoding Unicode
            } else {
                Add-Content -Path "$LogFile" -Value "(none)`r`n" -Encoding Unicode
            }
	        Add-Content -Path "$LogFile" -Value "`r`nApply Package Error Users:" -Encoding Unicode
	        Add-Content -Path "$LogFile" -Value "--------------------------" -Encoding Unicode
            if ( @($ApplyErrorUser).count -gt 0) {
    	        #Process-UserCSVOutput ($ApplyErrorUser | Select -property * -exclude RequiredPackage, SettingsSource, Error, Description, Filter, BatchJob, UserCount, ErrorInstance, FailedUserToBeRemovedFromLicensingGroup | Sort PackageType, EmailAddress)
				Process-UserCSVOutput ($ApplyErrorUser | where {$true} -Pipelinevariable UserC | Select -Property *, @{name="PackageInstances"; expression={ @($AssignmentErrors | where {$_.ObjectID -eq $UserC.ObjectID -and $_.State -gt -3}).count }} | Select -property * -exclude RequiredPackage, SettingsSource, Error, Description, Filter, FilterTypeSource, BatchJob, UserCount, ErrorInstance, State, FailedUserToBeRemovedFromLicensingGroup | Sort @{Expression = "PackageType"; Descending = $true}, EmailAddress)
            } else {
                Add-Content -Path "$LogFile" -Value "(none)`r`n" -Encoding Unicode
            }
        }
        if ( @($AssignmentErrors).count -gt 0) {
			write-host "$($AssignmentErrors.count) Apply Package Error Instances were encountered during Package Processing, with $($ApplyErrorUser.count) users affected" -ForegroundColor Red
			$warnings += "- $($AssignmentErrors.count) Apply Package Error Instances were encountered during Package Processing, with $($ApplyErrorUser.count) users affected"

            $IssueSummary += [PSCustomObject]@{
                'IssueType' = 'Apply Package Errors - Instances'
                'Teacher' =   @($AssignmentErrors | where { $_.PackageType -eq 'Teacher'}).count
                'Student' =  @($AssignmentErrors | where { $_.PackageType -eq 'Student'}).count
                'Total' = $AssignmentErrors.count
            }

            $IssueSummary += [PSCustomObject]@{
                'IssueType' = 'Apply Package Error - Users'
                'Teacher' =   @($ApplyErrorUser | where { $_.PackageType -eq 'Teacher'}).count
                'Student' =  @($ApplyErrorUser | where { $_.PackageType -eq 'Student'}).count
                'Total' = $ApplyErrorUser.Count
            }
        }

        $UsersToDeLicense+= @($NotEnabledUser | where { $_.FailedUserToBeRemovedFromLicensingGroup -eq $true } | Sort-Object -Property ObjectID -Unique | Select @{N=’UserType’; E={$_.PackageType}}, EmailAddress, DisplayName, ObjectID, @{N=’Problem’; E={'Not Enabled'}}, @{N=’Source’; E={'Package Processing'}}, @{N=’Settings’; E={$_.Settings}}, @{N=’CollectionID’; E={$_.CollectionID}}, @{N=’State’; E={$_.State}}) # 1.0.94
        $UsersToDeLicense+= @($NotLicensedUser | where { $_.FailedUserToBeRemovedFromLicensingGroup -eq $true } | Sort-Object -Property ObjectID -Unique | Select @{N=’UserType’; E={$_.PackageType}}, EmailAddress, DisplayName, ObjectID, @{N=’Problem’; E={'Not Licensed'}}, @{N=’Source’; E={'Package Processing'}}, @{N=’Settings’; E={$_.Settings}}, @{N=’CollectionID’; E={$_.CollectionID}}, @{N=’State’; E={$_.State}}) # 1.0.94
        $UsersToDeLicense+= @($ApplyErrorUser | where { $_.FailedUserToBeRemovedFromLicensingGroup -eq $true } | Sort-Object -Property ObjectID -Unique | Select @{N=’UserType’; E={$_.PackageType}}, EmailAddress, DisplayName, ObjectID, @{N=’Problem’; E={'Assignment Error'}}, @{N=’Source’; E={'Package Processing'}}, @{N=’Settings’; E={$_.SettingsSource}}, @{N=’CollectionID’; E={$_.CollectionID}}, @{N=’State’; E={$_.State}}) # 1.0.94
        $UsersToDeLicense = @($UsersToDeLicense | Sort-Object -Property ObjectID -Unique) # 1.0.94

        $results += [PSCustomObject]@{
            'Type' = "Package Processing - $($TeacherPoliciesProcessed.Count) Teacher Packages"
            'Quantity' = $TeacherQty
            'Source' = "$TeacherPolicies Packages"
            'Action' = if ($TeacherQty -gt 0) { "$($SimString)Packages Applied or Submitted" } else { "$($SimString)Packages Processed" }
            'Target' = 'Teachers'
            'OtherInfo' = ''
            #'AssignmentErrors' = ($AssignmentErrors | where { $_.PackageType -eq 'Teacher'  } | Select UserCount | Measure-Object -sum).sum
            'AssignmentErrors' = @($ApplyErrorUser | where { $_.PackageType -eq 'Teacher'}).count
        }

        $results += [PSCustomObject]@{
            'Type' = "Package Processing - $($StudentPoliciesProcessed.Count) Student Packages"
		    'Quantity' = $StudentQty
		    'Source' = "$StudentPolicies Packages"
            'Action' = if ($StudentQty -gt 0) { "$($SimString)Packages Applied or Submitted" } else { "$($SimString)Packages Processed" }
            'Target' = 'Students'
            'OtherInfo' = ''
            #'AssignmentErrors' = ($AssignmentErrors | where { $_.PackageType -eq 'Student'  } | Select UserCount | Measure-Object -sum).sum
            'AssignmentErrors' =  @($ApplyErrorUser | where { $_.PackageType -eq 'Student'}).count
        }

    if ($DetailedLoggingEnabled) {
		
		if ($iniFile.Misc.GetExtraStatistics -ne $false -and $iniFile.Misc.GetExtraStatistics -ne 0 ) {
				write-host "Getting Extra Package Statistics (this may take a few minutes)" -foregroundcolor green 
				Add-Content -Path "$LogFile" -Value "`r`nUser Package Summary:" -Encoding Unicode
				Add-Content -Path "$LogFile" -Value "---------------------" -Encoding Unicode
				
				$obj = @($UserOutput  | Sort-Object -Property ObjectID -Unique) # 1.0.94

				$UserSummary = @()
				$PackageSummaryInstance = @()
				
				[int]$ResultCount=0
				[int]$personprocessed=0
				$funcstarttime = (Get-Date)
				foreach ($littleobj in $obj) {
					$ResultCount+=1
					$personprocessed = $personprocessed + 1
					$PackageSummaryInstance = @($UserOutput | where {$_.ObjectID -eq $littleobj.ObjectID})
					$UserSummary+= [PSCustomObject]@{
						'DisplayName' = $littleobj.DisplayName
						'EmailAddress' = $littleobj.EmailAddress
						'IsLicensed' = $littleobj.IsLicensed
						'Enabled' = $littleobj.Enabled
						'ObjectID' = $littleobj.ObjectID
						'PackageCorrect' = @($PackageSummaryInstance | where {  $_.State -eq 3}).count
						'PackageUpdated' = @($PackageSummaryInstance | where { $_.State -eq 2}).count
						'Group, AD OU/CN or Attribute Filtered' = @($PackageSummaryInstance | where { ($_.State -eq 0 -or $_.State -eq 1 )}).count
						'FilteredByCollectionID' = @($PackageSummaryInstance | where { $_.State -eq -3}).count
						'DisabledorNotLicensed' = @($PackageSummaryInstance | where { ($_.State -eq -2 -or $_.State -eq -1 )}).count
						'PendingRemediation' = if ( $UsersToDeLicense.count -gt 0 ) {  if ( @($UsersToDeLicense | where { $_.ObjectID -eq $littleobj.ObjectID }).count -gt 0) { $true } else {$false} } else { '-' }
					}
					if ($ResultCount -eq $LongJobUpdateInterval) {

						[int]$ResultCount=0

						$UsersProcessedString, $timestring, $timeremainingseconds = get-timeelapsed -StartTime $funcstarttime -Processed $personprocessed -ToProcess ($obj.Count)

						if ($timeremainingseconds -ge 10) {
							write-host "Getting Extra Package Statistics / User Package Summary - Processed $UsersProcessedString of $($obj.Count) source users - ETR$timestring" -ForegroundColor Gray
							if ($DetailedLoggingEnabled) {
								Add-Content -Path "$LogFile" -Value "Getting Extra Package Statistics / User Package Summary - Processed $UsersProcessedString of $($obj.Count) source users - ETR$timestring" -Encoding Unicode  
							}
						}
					}
				}
				
				Process-UserCSVOutput ($UserSummary | Sort @{Expression = "PackageType"; Descending = $true}, EmailAddress)

				foreach ($Usertype in @("*","Teacher","Student")) {
					if ($UserType -eq '*') {
						Add-Content -Path "$LogFile" -Value "`r`nAll Users Package Summary:" -Encoding Unicode
						Add-Content -Path "$LogFile" -Value "--------------------------" -Encoding Unicode
					} else {
						Add-Content -Path "$LogFile" -Value "`r`n$Usertype Package Summary:" -Encoding Unicode
						Add-Content -Path "$LogFile" -Value "------------------------" -Encoding Unicode
					}
					
					$PackageSummaryInstance = @()
					$PackageSummary = @()
					$PackageSummaryInstance = @($UserOutput | where { $_.PolicyType -like "$Usertype"})
					$PackageSummaryUsers = @($PackageSummaryInstance | Sort-Object -Property ObjectID -Unique)
					$PackageSummary+= [PSCustomObject]@{
						'Number of Package Requests' = @($PackagesToProcess | where {$_.UserType -like "$Usertype"}).count							
						'PackageCorrectInstances' = @($PackageSummaryInstance | where { $_.State -eq 3 } ).count
						'PackageCorrectUsers' = @($PackageSummaryInstance | where { $_.State -eq 3 } | Sort-Object -Property ObjectID -Unique ).count # 1.0.94
						'PackageUpdatedInstances' = @($PackageSummaryInstance | where { $_.State -eq 2 } ).count
						'PackageUpdatedUsers' = @($PackageSummaryInstance | where { $_.State -eq 2 } | Sort-Object -Property ObjectID -Unique ).count # 1.0.94
						'Group, AD OU/CN or Attribute Filtered Instances' = @($PackageSummaryInstance | where { ($_.State -eq 0 -or $_.State -eq 1 )} ).count
						'Group, AD OU/CN or Attribute Filtered Users' = @($PackageSummaryInstance | where { ($_.State -eq 0 -or $_.State -eq 1 )} | Sort-Object -Property ObjectID -Unique ).count # 1.0.94
						'FilteredByCollectionIDInstances' = @($PackageSummaryInstance | where { $_.State -eq -3} ).count
						'FilteredByCollectionIDUsers' = @($PackageSummaryInstance | where { $_.State -eq -3} | Sort-Object -Property ObjectID -Unique ).count # 1.0.94
						'MatchingLicensedUsersThatAreNotEnabledInstances' = @($PackageSummaryInstance | where {  $_.Enabled -ne $true -and $_.IsLicensed -eq $true -and ($_.State -ne 0 -and $_.State -ne 1 ) } ).count # 1.0.94
						'MatchingLicensedUsersThatAreNotEnabledUsers' = @($PackageSummaryInstance | where {  $_.Enabled -ne $true -and $_.IsLicensed -eq $true -and ($_.State -ne 0 -and $_.State -ne 1 )} | Sort-Object -Property ObjectID -Unique ).count # 1.0.94
						'AllLicensedUsersThatAreNotEnabledInstances' = @($PackageSummaryInstance | where {  $_.Enabled -ne $true -and $_.IsLicensed -eq $true } ).count # 1.0.94
						'AllLicensedUsersThatAreNotEnabledUsers' = @($PackageSummaryInstance | where {  $_.Enabled -ne $true -and $_.IsLicensed -eq $true } | Sort-Object -Property ObjectID -Unique ).count # 1.0.94
					}
					
					$PackageSummary | ft * -AutoSize | Out-String -Width 4096 | Out-File -filepath "$LogFile" -append -Encoding Unicode
					
					$correct = $null
					$updated = $null
					[int]$correct = $PackageSummary[0].PackageCorrectInstances
					[int]$updated = $PackageSummary[0].PackageUpdatedInstances
					
					$QuickSummary = @()
					
					$QuickSummary+= [PSCustomObject]@{
						'UserType' = if ($UserType -eq '*') { "All" } else { "$UserType"}
						'Metric' = 'Instances'
						'Measurement' = 'Total Correct'
						'Quantity' = $correct
					}
					
					$QuickSummary+= [PSCustomObject]@{
						'UserType' = if ($UserType -eq '*') { "All" } else { "$UserType"}
						'Metric' = 'Instances'
						'Measurement' = 'Total Updated'
						'Quantity' = $updated
					}					
					
					$QuickSummary+= [PSCustomObject]@{
						'UserType' = if ($UserType -eq '*') { "All" } else { "$UserType"}
						'Metric' = 'Instances'
						'Measurement' = 'Total Correct and Updated Instances'
						'Quantity' = $correct + $updated
					}
					
					$correct = $null
					$updated = $null
					[int]$correct = $PackageSummary[0].PackageCorrectUsers
					[int]$updated = $PackageSummary[0].PackageUpdatedUsers

					$QuickSummary+= [PSCustomObject]@{
						'UserType' = if ($UserType -eq '*') { "All" } else { "$UserType"}
						'Metric' = 'Users'
						'Measurement' = 'Total Correct'
						'Quantity' = $correct
					}
					
					$QuickSummary+= [PSCustomObject]@{
						'UserType' = if ($UserType -eq '*') { "All" } else { "$UserType"}
						'Metric' = 'Users'
						'Measurement' = 'Total Updated'
						'Quantity' = $updated
					}					
					
					$QuickSummary+= [PSCustomObject]@{
						'UserType' = if ($UserType -eq '*') { "All" } else { "$UserType"}
						'Metric' = 'Users'
						'Measurement' = 'Total Correct and Updated Users'
						'Quantity' = $correct + $updated
					}
					$QuickSummary | ft * | Out-String -Width 4096 | Out-File -filepath "$LogFile" -append -Encoding Unicode
				}
			}		
		
            Add-Content -Path "$LogFile" -Value "`r`nPackage Results:" -Encoding Unicode
            Add-Content -Path "$LogFile" -Value "----------------" -Encoding Unicode
            if ( @($Output).count -gt 0) {
                $Output | ft * -AutoSize | Out-String -Width 4096 | Out-File -filepath "$LogFile" -append -Encoding Unicode
            } else {
                Add-Content -Path "$LogFile" -Value "(none)`r`n" -Encoding Unicode
            }
            Add-Content -Path "$LogFile" -Value "$($SimString)$($Output.Count) Package Requests Processed with $TeacherQty Teacher Package Changes and $StudentQty Student Package Changes`r`n" -Encoding Unicode   
        }

        if ($DetailedLoggingEnabled) {
            Add-Content -Path "$LogFile" -Value "`r`nPackage Issue Summary:" -Encoding Unicode
            Add-Content -Path "$LogFile" -Value "----------------------" -Encoding Unicode
            if ( @($IssueSummary).count -gt 0) {
				
				$TotalUserIssues = @()
				$TotalUserIssues = @(@($NotEnabledUser | Select PolicyType, ObjectID) + @($NotLicensedUser | Select PolicyType, ObjectID) + @($ApplyErrorUser | Select PolicyType, ObjectID) | Sort-Object -Property ObjectID -Unique) # 1.0.94
				

				$IssueSummary += [PSCustomObject]@{
                    'IssueType' = 'TOTAL INSTANCES'
                    'Teacher' =   ($IssueSummary | where { $_.IssueType -like "* - Instances" } | Select -Expand Teacher | Measure-Object -sum).sum
                    'Student' =  ($IssueSummary | where { $_.IssueType -like "* - Instances" } | Select -Expand Student | Measure-Object -sum).sum
                    'Total' = ($IssueSummary | where { $_.IssueType -like "* - Instances" } | Select -Expand Total | Measure-Object -sum).sum
                }
                $IssueSummary += [PSCustomObject]@{
                    'IssueType' = 'TOTAL USERS'
                    'Teacher' =   ($IssueSummary | where { $_.IssueType -like "* - Users" } | Select -Expand Teacher | Measure-Object -sum).sum
                    'Student' =  ($IssueSummary | where { $_.IssueType -like "* - Users" } | Select -Expand Student | Measure-Object -sum).sum
                    'Total' = ($IssueSummary | where { $_.IssueType -like "* - Users" } | Select -Expand Total | Measure-Object -sum).sum
                }
                $IssueSummary += [PSCustomObject]@{
                    'IssueType' = 'TOTAL UNIQUE USERS'
                    'Teacher' =   @($TotalUserIssues | where { $_.PolicyType -eq 'Teacher'}).count
                    'Student' =  @($TotalUserIssues | where { $_.PolicyType -eq 'Student'}).count
                    'Total' = $TotalUserIssues.Count
                }				
                $IssueSummary | ft * -AutoSize | Out-String -Width 4096 | Out-File -filepath "$LogFile" -append -Encoding Unicode
            } else {
                Add-Content -Path "$LogFile" -Value "(none)`r`n" -Encoding Unicode
            }
        }

        if ($DetailedLoggingEnabled) {
		    Add-Content -Path "$LogFile" -Value "`r`nPackage Batch Submission Summary:" -Encoding Unicode
            Add-Content -Path "$LogFile" -Value "---------------------------------" -Encoding Unicode	
		
			if ($PackageBatchesSubmitted.count -gt 0) {
				        $GoodBatches = @($PackageBatchesSubmitted | where { $_.Error -eq '(no error)' })
						$BadBatches = @($PackageBatchesSubmitted | where { $_.Error -ne '(no error)' })
						$GoodBatchChanges = ($GoodBatches.UsersInBatch | Measure-Object -sum).sum
						$BadBatchChanges = ($BadBatches.UsersInBatch | Measure-Object -sum).sum
				
				
				$PackageBatchesSubmitted | ft * -AutoSize | Out-String -Width 4096 | Out-File -filepath "$LogFile" -append -Encoding Unicode
				Add-Content -Path "$LogFile" -Value "$($SimString)$($GoodBatches.Count) Package Batch Jobs sucessfully submitted containing $GoodBatchChanges user modifications" -Encoding Unicode
				Add-Content -Path "$LogFile" -Value "$($SimString)$($BadBatches.Count) Package Batch Jobs failed to be submitted containing $BadBatchChanges user modifications`r`n" -Encoding Unicode
			} else {
				Add-Content -Path "$LogFile" -Value "(none)`r`n" -Encoding Unicode	
			}
		}

        if ($DetailedLoggingEnabled) {
            Add-Content -Path "$LogFile" -Value "`r`n" -Encoding Unicode
        }

        $Actions += "- $($SimString)$($Output.Count) Package Requests Processed with $TeacherQty Teacher Package Changes and $StudentQty Student Package Changes"
        write-host "$($SimString)$($Output.Count) Package Requests Processed with $TeacherQty Teacher Package Changes and $StudentQty Student Package Changes" -ForegroundColor $alttextcolour

        if ($DetailedLoggingEnabled) {
            Add-Content -Path "$LogFile" -Value "`r`n" -Encoding Unicode
        }


		if ($ApplytoAllTeachersOnNextRunThisFunc -gt 0 -or $ApplytoAllStudentsOnNextRunThisFunc -gt 0) {
			$ApplytoAllonNextRunSummary += [PSCustomObject]@{
				'Function' = 'Package Processing'
				'Teachers' = $ApplytoAllTeachersOnNextRunThisFunc 
				'Students' = $ApplytoAllStudentsOnNextRunThisFunc
			}
		}
		if ($ApplytoAllTeachersOnNextRunThisFunc -gt 0) { $ApplytoAllTeachersOnNextRun=$true }
		if ($ApplytoAllStudentsOnNextRunThisFunc -gt 0) { $ApplytoAllStudentsOnNextRun=$true }


    } else {
        write-host "There are no Filtered Teams Packages to Apply" -foregroundcolor white
        $Actions += "- There are no Filtered Teams Packages to Apply"
        if ($DetailedLoggingEnabled) {
            Add-Content -Path "$LogFile" -Value "`r`nApplying Filtered Teams Packages" -Encoding Unicode
            Add-Content -Path "$LogFile" -Value "================================`r`n" -Encoding Unicode
            Add-Content -Path "$LogFile" -Value "Packages to Process:" -Encoding Unicode
            Add-Content -Path "$LogFile" -Value "--------------------" -Encoding Unicode
            Add-Content -Path "$LogFile" -Value "`r`nThere are no Filtered Teams Packages to Apply" -Encoding Unicode
        }
    }
} else {
	if ($DetailedLoggingEnabled) {
		Add-Content -Path "$LogFile" -Value "`r`nFiltered Teams Packages" -Encoding Unicode
		Add-Content -Path "$LogFile" -Value "=======================`r`n" -Encoding Unicode
		Add-Content -Path "$LogFile" -Value "Packages that would be Processed:" -Encoding Unicode
		Add-Content -Path "$LogFile" -Value "---------------------------------" -Encoding Unicode
		if ($PackagesToProcess.Count -gt 0) {
			$PackagesToProcess | select -Property * -ExcludeProperty CollectionIDsToExclude | ft * -AutoSize | Out-String -Width 4096 | Out-File -filepath "$LogFile" -append -Encoding Unicode # 1.0.94
			Add-Content -Path "$LogFile" -Value "Note: If the global filter type setting is set to Remote, the filter source is forced to CSOnline`r`n" -Encoding Unicode
		} else {
			Add-Content -Path "$LogFile" -Value "`r`nThere are no Filtered Teams Packages to Apply" -Encoding Unicode
		}
	}	
}

# process policies
if ($MajorErrors.Count -eq 0) {
    $AssignmentErrorsCaptured=0
    $DisabledUsersErrorsCaptured=0
	[int]$ApplytoAllTeachersOnNextRunThisFunc=0
	[int]$ApplytoAllStudentsOnNextRunThisFunc=0
    if ($PoliciestoProcess.Count -gt 0) {
        write-host "Applying Filtered Teams Policies" -foregroundcolor green        
        if ($DetailedLoggingEnabled) {
            Add-Content -Path "$LogFile" -Value "`r`nApplying Filtered Teams Policies" -Encoding Unicode
            Add-Content -Path "$LogFile" -Value "================================`r`n" -Encoding Unicode
            Add-Content -Path "$LogFile" -Value "Policies to Process:" -Encoding Unicode
            Add-Content -Path "$LogFile" -Value "--------------------" -Encoding Unicode
            $PoliciestoProcess | select -Property * -ExcludeProperty CollectionIDsToExclude | ft * -AutoSize | Out-String -Width 4096 | Out-File -filepath "$LogFile" -append -Encoding Unicode # 1.0.94
			Add-Content -Path "$LogFile" -Value "Note: If the global filter type setting is set to Remote, the filter source is forced to CSOnline`r`n" -Encoding Unicode
        }

        [int]$TeacherTeamsPolicyBatchProcessingThreshold=99999

        if ($iniFile.Misc.TeacherTeamsPolicyBatchProcessingThreshold) {
            [int]$TeacherTeamsPolicyBatchProcessingThreshold=$iniFile.Misc.TeacherTeamsPolicyBatchProcessingThreshold -as [int]
		}
		if ($DetailedLoggingEnabled) {
            Add-Content -Path "$LogFile" -Value "Teacher Teams Policy Batch Processing Threshold: $TeacherTeamsPolicyBatchProcessingThreshold" -Encoding Unicode
        }
        $Information+= "- Teacher Teams Policy Batch Processing Threshold: $TeacherTeamsPolicyBatchProcessingThreshold"
        

        [int]$StudentTeamsPolicyBatchProcessingThreshold=99999

        if ($iniFile.Misc.StudentTeamsPolicyBatchProcessingThreshold) {
            [int]$StudentTeamsPolicyBatchProcessingThreshold=$iniFile.Misc.StudentTeamsPolicyBatchProcessingThreshold -as [int]
		}
		if ($DetailedLoggingEnabled) {
            Add-Content -Path "$LogFile" -Value "Student Teams Policy Batch Processing Threshold: $StudentTeamsPolicyBatchProcessingThreshold" -Encoding Unicode
        }
        $Information+= "- Student Teams Policy Batch Processing Threshold: $StudentTeamsPolicyBatchProcessingThreshold"
        


        if ($DetailedLoggingEnabled) {
            Add-Content -Path "$LogFile" -Value "Maximum Batch Sizes for Policy and Package Processing: $MaximumBatchSize" -Encoding Unicode
            Add-Content -Path "$LogFile" -Value "`r`n"
        }

        [int]$PoliciesProcessed=0
        $Filter=$null
		$FilterSource=$null
        $OnlineUsers = @()

		$AllCSOnlineUsers = @()
		$AllAzureADUsers = @()

        $Output= @()
		$onlineobj = $null
		$AssignmentErrors = @()
        $UserOutput = @()
        
        [int]$ActionTypeMaxLength=($PoliciesToProcess | Select ActionType | Sort -Property { $_.Actiontype.length} | select -Last 1).Actiontype.Length
        [int]$ActionPolicyMaxLength=($PoliciesToProcess | Select ActionPolicy | Sort -Property { $_.ActionPolicy.length} | select -Last 1).ActionPolicy.Length
		
		[int]$SettingsMaxLength=($PoliciesToProcess | Select Settings | Sort -Property { $_.Settings.length} | select -Last 1).Settings.Length
		[int]$CollectionIDMaxLength=(($PoliciesToProcess | Select CollectionID | Sort -Property { $_.CollectionID} | select -Last 1).CollectionID -as [string]).length
        
		$CurrentlyUnlicensedUsers = @()
		$CurrentlyUnlicensedUsers = Get-MsolUser -All -UnlicensedUsersOnly

        $PreviousSettings = $null
		
        foreach ($Policy in $PoliciestoProcess) {
            $UsersToApply = @()
            $AssignmentErrorsThisPolicy=0
            $DisabledUsersThisPolicy=0
            $PoliciesProcessed = $PoliciesProcessed +1

			$RemoveLGForAssignmentErrors = switch($Policy.RemoveFailedUsersFromLicensingGroup -band 2) { 2 {$true}; default {$false}}
			$RemoveLGForDisabledUsers = switch($Policy.RemoveFailedUsersFromLicensingGroup -band 4) { 4 {$true}; default {$false}}
			$RemoveLGForNotLicensedUsers = switch($Policy.RemoveFailedUsersFromLicensingGroup -band 8) { 8 {$true}; default {$false}}

    
            if ($Policy.Description) {
                $DescString = "$($Policy.Description)"
            } else {
                $DescString = "-"
            }

            $ProcessedString = [string]$PoliciesProcessed
			$ProcessedString = $ProcessedString.PadLeft(($PoliciestoProcess.Count -as [string]).length," ")
			$PadSettings=$Policy.Settings.PadRight($SettingsMaxLength," ")
			$PadCollectionID=([string]$Policy.CollectionID).PadRight($CollectionIDMaxLength," ")
            
            $PadActionType=$Policy.ActionType.PadRight($ActionTypeMaxLength," ")
            $PadActionPolicy=$Policy.ActionPolicy.PadRight($ActionPolicyMaxLength," ")

            write-host "$($SimString)Processing Policy Request $ProcessedString of $($PoliciestoProcess.Count): CollectionID: $PadCollectionID   $PadSettings   Policy Type: $PadActionType   Policy Name: $PadActionPolicy   Desc: $DescString" -ForegroundColor $textcolour

            if ($DetailedLoggingEnabled) {
                Add-Content -Path "$LogFile" -Value "`r`n$($SimString)Processing Policy Request $ProcessedString of $($PoliciestoProcess.Count): CollectionID: $PadCollectionID   $PadSettings   Policy Type: $PadActionType   Policy Name: $PadActionPolicy   Desc: $DescString" -Encoding Unicode
            }
			$string=$null
			$string=""
            if ($Policy.UserType -eq 'Student') {
                if ($Policy.ApplyToAll -eq $true -or $ApplytoAllStudents -eq $true) {
                    $UsersToProcess = $StudentList
                } else {
                    $UsersToProcess = $StudentsNotInLicensingGroup
					$string=" that were not members of the Licensing Group"
                }
            }
            if ($Policy.UserType -eq 'Teacher') {
                if ($Policy.ApplyToAll -eq $true -or $ApplytoAllTeachers -eq $true) {
                    $UsersToProcess = $TeacherList
                } else {
                    $UsersToProcess = $TeachersNotInLicensingGroup
					$string=" that were not members of the Licensing Group"
                }
            }

			if ($Policy.DomainFilter ) {
				$UsersToProcess = @($UsersToProcess | where { $Policy.DomainFilter -contains $_.EmailAddress.Split('@')[1] })
				$string="$string and were filtered by the Domain Filter"
			}

            if ($Policy.OnlyApplyToUnlicensed -eq $true) {
                $UsersToProcess = @($UsersToProcess | where { $_.IsLicensed -eq $false })
				
				write-host "$($SimString)Using $($UsersToProcess.count) Unlicensed Source List $($Policy.UserType)s$String" -ForegroundColor $textcolour	
				if ($DetailedLoggingEnabled) {
					Add-Content -Path "$LogFile" -Value "$($SimString)Using $($UsersToProcess.count) Unlicensed Source List $($Policy.UserType)s$String"
				}
            } else {
				write-host "$($SimString)Using $($UsersToProcess.count) Source List $($Policy.UserType)s$String" -ForegroundColor $textcolour	
				if ($DetailedLoggingEnabled) {
					Add-Content -Path "$LogFile" -Value "$($SimString)Using $($UsersToProcess.count) Source List $($Policy.UserType)s$String"
				}	
			}
			$string=$null

            if ($Policy.Settings -ne $PreviousSettings) {
				$RequiredRecursiveIDs = @()
                $NotRequiredRecursiveIDs = @()
                $ProblemGroups = @()
                $GroupRulesOK = $true
				$GotGroupUsersOK = $true
				$GroupMatchedUsers = @()
                if ($Policy.HasGroupRules -eq $true) {

                    write-host "$($SimString)Building Group Rules Table" -ForegroundColor $textcolour

                    if ($DetailedLoggingEnabled) {
                        Add-Content -Path "$LogFile" -Value "`r`n$($SimString)Building Group Rules Table`r`n" -Encoding Unicode
                    }
					$e = $error.count
                    $GroupRulesOK, $ProblemGroups, $RequiredRecursiveIDs, $RequiredORRecursiveIDs, $NotRequiredRecursiveIDs = BuildGroupTable -GroupInformation $Policy

                    if ($GroupRulesOK -eq $true) {
                        write-host "$($SimString)Group Rules OK: $GroupRulesOK   Problem Groups: $($ProblemGroups.count)   RequiredGroups: $($RequiredRecursiveIDs.count)   RequiredOrGroups: $($RequiredORRecursiveIDs.count)   NotRequiredGroups: $($NotRequiredRecursiveIDs.count)" -ForegroundColor $textcolour
						$GroupMatchedUsers = GetGroupRuleUsers -RequiredRecursiveIDs $RequiredRecursiveIDs -RequiredORRecursiveIDs $RequiredORRecursiveIDs -NotRequiredRecursiveIDs $NotRequiredRecursiveIDs
						write-host "$($SimString)Found $($GroupMatchedUsers.count) users matching the Group Rules" -ForegroundColor $textcolour

                    } else {
                        write-host "$($SimString)Group Rules OK: $GroupRulesOK   Problem Groups: $($ProblemGroups.count)   RequiredGroups: $($RequiredRecursiveIDs.count)   RequiredOrGroups: $($RequiredORRecursiveIDs.count)   NotRequiredGroups: $($NotRequiredRecursiveIDs.count)" -ForegroundColor yellow
                        $warnings += "- $($SimString)Policy $($PoliciesProcessed): Group Rules OK: $GroupRulesOK   Problem Groups: $($ProblemGroups.count)   RequiredGroups: $($RequiredRecursiveIDs.count)   RequiredOrGroups: $($RequiredORRecursiveIDs.count)   NotRequiredGroups: $($NotRequiredRecursiveIDs.count)"
                    }
					if ($error.count -gt $e) {
						$errorstring = ""
						if ($Error[0].Exception.Message) {
							$errorstring = $Error[0].Exception.Message
						} else {
							$errorstring = "(unknown)"
						}
						write-host "There was an error in processing Group Rules or matching users - error message: $errorstring" -foreground red
						if ($DetailedLoggingEnabled) {
							Add-Content -Path "$LogFile" -Value "There was an error in processing Group Rules or matching users - error message: $errorstring" -Encoding Unicode
						}
						$Warnings += "- $($SimString)Policy $($PoliciesProcessed): There was an error in processing Group Rules or matching users - error message: $errorstring"
						$GotGroupUsersOK = $false
					}
                    if ($DetailedLoggingEnabled) {
						if ($RequiredRecursiveIDs.count -gt 0) {
							Add-Content -Path "$LogFile" -Value "Group Rules: RequiredGroups, Including Recursive"
							Add-Content -Path "$LogFile" -Value "------------------------------------------------"
							$RequiredRecursiveIDs | Sort DisplayName,  CommonName, Description, ObjectID | fl * | Out-String -Width 4096 | Out-File -filepath "$LogFile" -append -Encoding Unicode
						}
						if ($RequiredORRecursiveIDs.count -gt 0) {
							Add-Content -Path "$LogFile" -Value "Group Rules: RequiredOrGroups, Including Recursive"
							Add-Content -Path "$LogFile" -Value "--------------------------------------------------"
							$RequiredORRecursiveIDs | Sort DisplayName,  CommonName, Description, ObjectID | fl * | Out-String -Width 4096 | Out-File -filepath "$LogFile" -append -Encoding Unicode
						}					
						if ($NotRequiredRecursiveIDs.count -gt 0) {
							Add-Content -Path "$LogFile" -Value "Group Rules: NotRequiredGroups, Including Recursive"
							Add-Content -Path "$LogFile" -Value "---------------------------------------------------"
							$NotRequiredRecursiveIDs | Sort DisplayName,  CommonName, Description, ObjectID | fl * | Out-String -Width 4096 | Out-File -filepath "$LogFile" -append -Encoding Unicode
						}
                        if ($ProblemGroups.count -gt 0) {
							Add-Content -Path "$LogFile" -Value "Group Rules: Problem Groups"
							Add-Content -Path "$LogFile" -Value "---------------------------"
                            $ProblemGroups | Sort DisplayName,  CommonName, Description, ObjectID | fl * | Out-String -Width 4096 | Out-File -filepath "$LogFile" -append -Encoding Unicode
                        }
                        Add-Content -Path "$LogFile" -Value "$($SimString)Group Rules OK: $GroupRulesOK   Problem Groups: $($ProblemGroups.count)   RequiredGroups: $($RequiredRecursiveIDs.count)   RequiredOrGroups: $($RequiredORRecursiveIDs.count)   NotRequiredGroups: $($NotRequiredRecursiveIDs.count)" -Encoding Unicode
						Add-Content -Path "$LogFile" -Value "$($SimString)Found $($GroupMatchedUsers.count) users matching the Group Rules" -Encoding Unicode						
                    }
                }
                $PreviousSettings=$Policy.Settings
            } else {
                if ($Policy.HasGroupRules -eq $true) {
					write-host "$($SimString)Reusing Previous Group Rules Table - $($GroupMatchedUsers.Count) Cached users matching the Group Rules" -ForegroundColor $textcolour
					if ($DetailedLoggingEnabled) {
						Add-Content -Path "$LogFile" -Value "$($SimString)Reusing Previous Group Rules Table - $($GroupMatchedUsers.Count) Cached users matching the Group Rules" -Encoding Unicode
					}
                }
            }
# end 1.0.21

			if ($Policy.HasADOURules -eq $true ) {
					write-host "$($SimString)Active Directory OU/CN Rules - Included OU/CN Or Rules: $($Policy.RequiredOrOUs.count)   Not Required OU/CN Rules: $($Policy.NotRequiredOUs.count)" -ForegroundColor $textcolour	
				
				if ($DetailedLoggingEnabled) {
					Add-Content -Path "$LogFile" -Value "`r`nActive Directory OU/CN Rules"
                    Add-Content -Path "$LogFile" -Value "----------------------------"
					$Policy | select RequiredOrOUs, NotRequiredOUs | Sort RequiredOrOUs, NotRequiredOUs | ft * -AutoSize | Out-String -Width 4096 | Out-File -filepath "$LogFile" -append -Encoding Unicode
	
					Add-Content -Path "$LogFile" -Value "$($SimString)Included OU/CN Or Rules: $($Policy.RequiredOrOUs.count)   Not Required OU/CN Rules: $($Policy.NotRequiredOUs.count)"
				}
			}

			if ($GotGroupUsersOK -eq $true) {
				if ($GroupRulesOK -eq $true) {

					$ActionType = $Policy.ActionType
					$PolicyExists = @()

					if ($ActionType -eq 'TeamsAppSetupPolicy') {
						$PolicyExists = Get-CsTeamsAppSetupPolicy -Identity $Policy.ActionPolicy -WarningAction SilentlyContinue -ErrorAction SilentlyContinue
					}
					if ($ActionType -eq 'TeamsAppPermissionPolicy') {
						$PolicyExists = Get-CsTeamsAppPermissionPolicy -Identity $Policy.ActionPolicy -WarningAction SilentlyContinue -ErrorAction SilentlyContinue
					}
					if ($ActionType -eq 'TeamsCallingPolicy') {
						$PolicyExists = Get-CsTeamsCallingPolicy -Identity $Policy.ActionPolicy -WarningAction SilentlyContinue -ErrorAction SilentlyContinue
					}
					if ($ActionType -eq 'TeamsChannelsPolicy') {
						$PolicyExists = Get-CsTeamsChannelsPolicy -Identity $Policy.ActionPolicy -WarningAction SilentlyContinue -ErrorAction SilentlyContinue
					}
					if ($ActionType -eq 'TeamsMeetingBroadcastPolicy') {
						$PolicyExists = Get-CsTeamsMeetingBroadcastPolicy -Identity $Policy.ActionPolicy -WarningAction SilentlyContinue -ErrorAction SilentlyContinue
					}
					if ($ActionType -eq 'TeamsMeetingPolicy') {
						$PolicyExists = Get-CsTeamsMeetingPolicy -Identity $Policy.ActionPolicy -WarningAction SilentlyContinue -ErrorAction SilentlyContinue
					}
					if ($ActionType -eq 'TeamsMessagingPolicy') {
						$PolicyExists = Get-CsTeamsMessagingPolicy -Identity $Policy.ActionPolicy -WarningAction SilentlyContinue -ErrorAction SilentlyContinue
					}
					if ($ActionType -eq 'TeamsFeedbackPolicy') {
						$PolicyExists = Get-CsTeamsFeedbackPolicy -Identity $Policy.ActionPolicy -WarningAction SilentlyContinue -ErrorAction SilentlyContinue
					}
					if ($PolicyExists) {
						if ($UsersToProcess.count -gt 0) {
							$e = $error.count
							#if ($UseLocalAttributeFiltering -eq $true -and $Policy.FilterSource -eq 'AzureAD') {
								if (!$AllCSOnlineUsers) {
									$AllCSOnlineUsers = @(Get-CSOnlineUser -ErrorAction SilentlyContinue -WarningAction SilentlyContinue)
								}
							#}
							if (!$AllAzureADUsers) {
								$AllAzureADUsers = @(AzureADPreview\Get-AzureADUser -all $true -ErrorAction SilentlyContinue -WarningAction SilentlyContinue)
							}
							if ($Policy.ActionFilter) {
								if ($Filter -ne $Policy.ActionFilter -or $FilterSource -ne $Policy.FilterSource) {
									$Filter = $Policy.ActionFilter
									$FilterSource = $Policy.FilterSource
									$FilterTypeSource = [string]$Policy.FilterType + " " + [string]$Policy.FilterSource
									if ($UseLocalAttributeFiltering -eq $false) {
										$OnlineUsers = @(Get-CSOnlineUser -Filter $Filter -ErrorAction SilentlyContinue -WarningAction SilentlyContinue)
										$ObjectField = 'Identity'
									} else {
										if ($FilterSource -eq 'AzureAD') {
											$OnlineUsers = @($AllAzureADUsers | where { Invoke-Expression $Filter })
											$ObjectField = 'ObjectID'
										} else {
											if (!$AllCSOnlineUsers) {
												$AllCSOnlineUsers = @(Get-CSOnlineUser -ErrorAction SilentlyContinue -WarningAction SilentlyContinue)
											}
											$OnlineUsers = @($AllCSOnlineUsers | where { Invoke-Expression $Filter })
											$ObjectField = 'Identity'
										}
									}
								}
							} else {
								$Filter = $null
								$FilterSource = $null
								$FilterTypeSource = '-'
								$OnlineUsers = @()
							}

							if ($error.count -eq $e) {
								[int]$ResultCount=0
								[int]$UsersProcessed=0
								$funcstarttime = (Get-Date)
								foreach ($user in $UsersToProcess) {
									$UserIsLicensed = $User.IsLicensed
									if ($UserIsLicensed -eq $false -and $CurrentlyUnlicensedUsers.ObjectID -notcontains $User.ObjectID) {
										$UserIsLicensed = $true
									}
									if ($Policy.SkipUpdatedLicensingCheck -eq $true -or $UserIsLicensed -eq $true) { # 1.0.94
										if ($Policy.HasGroupRules -eq $false -or ($GroupMatchedUsers.ObjectID -contains $User.ObjectID.Guid)) { # 1.0.25 hopefully improved processing time
											$onlineobj = $null
											if ($Policy.ActionFilter) {
												$onlineobj = $OnlineUsers | where { $_.($ObjectField) -eq $User.ObjectID } | select -First 1 # 1.0.84
											}
											if (!$Filter -or $onlineobj) {
												# $onlineobj at this point is an AzureAD obj if $UseAzureADforLocalAttributeFilteringByDefault -eq $true and $UseLocalAttributeFiltering -eq $true
												# otherwise $onlineobj is a CSOnline Obj
											
		# msonline - Email address and ObjectID
		# csonline - UserPrincipalName and Identity
		# AzureAD - UserPrincipalName  and ObjectID
		
													# both are required below
												if ($FilterSource -eq 'AzureAD' -and $UseLocalAttributeFiltering -eq $true) {
													# we already have the AD in the wrong place and Need CSOnline
													$ADObject = $null
													$ADObject = $onlineobj
													$onlineobj = $AllCSOnlineUsers | where { $_.Identity -eq $onlineobj.ObjectID } | select -first 1
												} else {
													# we already have CSOnline and need ADObject
													$ADObject = $null
													$ADObject = $AllAzureADUsers | where { $_.ObjectID -eq $User.ObjectID } | select -first 1
													if (!$onlineobj) {
														$onlineobj = $AllCSOnlineUsers | where {$_.Identity -eq $User.objectid }  | select -First 1	
													}
												}
												
												$UserPolicyContent = $null
												$UserPolicyContent=$OnlineObj.$ActionType.Name
		
												if ($Policy.HasADOURules -eq $true) {
													$OU = $null
													$OU = ($AllAzureADUsers | where {$_.ObjectID -eq $User.ObjectID}).ExtensionProperty.onPremisesDistinguishedName
												}

												if ($Policy.HasADOURules -eq $false -or ( (Check_ADCanonicalName -UserCN $OU -RequiredOrOUs $Policy.RequiredOrOUs -NotRequiredOUs $Policy.NotRequiredOUs) -eq $true)) {
		

													#if (($OnlineObj.$ActionType.ToUpper() -ne $Policy.ActionPolicy.ToUpper() -and $Policy.ApplyOnlyIfExistingPolicyBlank -eq $false) -or ($Policy.ApplyOnlyIfExistingPolicyBlank -eq $true -and $User.$ActionType -eq $null)) {
													if (($UserPolicyContent -ne $Policy.ActionPolicy -and $Policy.ApplyOnlyIfExistingPolicyBlank -eq $false) -or ($Policy.ApplyOnlyIfExistingPolicyBlank -eq $true -and $UserPolicyContent -eq $null)) {

														#$UsersToApply += $User.EmailAddress
														#if ($Policy.ProcessDisabledUsers -eq $true -or $onlineobj.Enabled -eq $true) {
														if ($Policy.ProcessDisabledUsers -eq $true -or $ADObject.AccountEnabled -eq $true) { # 1.0.61

															if ($Policy.CollectionID -eq 0 -or ( @($UserOutput | where {$_.CollectionID -gt $Policy.CollectionIDIgnoreThreshold -and $_.CollectionID -lt $Policy.CollectionID -and $_.CollectionID -notin @($Policy.CollectionIDsToExclude) -and $_.ObjectID -eq $User.ObjectID -and $_.Policy -eq $Policy.ActionType -and ($_.UserType -eq $Policy.UserType -or $IgnoreCollectionIDUserType -eq $true) -and $_.State -gt 1 }).count -eq 0)) { #1.0.86
																$UsersToApply += $User # 1.0.26
																#user to be processed, add output here

																$UserOutput += [PSCustomObject]@{
																	'PolicyType' = $Policy.UserType
																	'Settings' = $Policy.Settings
																	'Default' = $Policy.Default
																	'CollectionID' = $Policy.CollectionID
																	#'CollectionIDIgnoreThreshold' = $Policy.CollectionIDIgnoreThreshold
																	'DisplayName' = $user.DisplayName
																	'EmailAddress' = $user.EmailAddress
																	'Status' = "Policy Update Required"
																	'IsLicensed' = $UserIsLicensed
																	'Enabled' = $ADObject.AccountEnabled 
																	'Policy' = $Policy.ActionType
																	'ExistingPolicy' =  $UserPolicyContent
																	'RequiredPolicy' = $Policy.ActionPolicy
																	'PolicyDescription' = $Policy.Description
																	#'PolicyFilter' = $Policy.ActionFilter
																	#'FilterTypeSource' = $FilterTypeSource 
																	#'CSorADOnlineUsers' = $OnlineUsers.Count
																	#'HasGroupRules' = $Policy.HasGroupRules
																	#'RequiredGroups' = $RequiredRecursiveIDs.count 
																	#'RequiredORGroups' = $RequiredORRecursiveIDs.count
																	#'NotRequiredGroups' = $NotRequiredRecursiveIDs.count
																	#'HasADOUCNRules' = $Policy.HasADOURules
																	#'RequiredORADOUCNs' = $Policy.RequiredOrOUs.count
																	#'NotRequiredADOUCNs' = $Policy.NotRequiredOUs.count
																	'ApplyOnlyIfExistingPolicyBlank' = $Policy.ApplyOnlyIfExistingPolicyBlank
																	'Simulation' = !$MakeChanges
																	'ObjectID' = $user.ObjectID
																	'State' = 2
																	'FailedUserToBeRemovedFromLicensingGroup' = $false
																}
															} else {
																$UserOutput += [PSCustomObject]@{
																	'PolicyType' = $Policy.UserType
																	'Settings' = $Policy.Settings
																	'Default' = $Policy.Default
																	'CollectionID' = $Policy.CollectionID
																	#'CollectionIDIgnoreThreshold' = $Policy.CollectionIDIgnoreThreshold
																	'DisplayName' = $user.DisplayName
																	'EmailAddress' = $user.EmailAddress
																	'Status' = "User filtered by CollectionID"
																	'IsLicensed' = $UserIsLicensed
																	'Enabled' = $ADObject.AccountEnabled 
																	'Policy' = $Policy.ActionType
																	'ExistingPolicy' =  $UserPolicyContent
																	'RequiredPolicy' = $Policy.ActionPolicy
																	'PolicyDescription' = $Policy.Description
																	#'PolicyFilter' = $Policy.ActionFilter
																	#'FilterTypeSource' = $FilterTypeSource
																	#'CSorADOnlineUsers' = $OnlineUsers.Count
																	#'HasGroupRules' = $Policy.HasGroupRules
																	#'RequiredGroups' = $RequiredRecursiveIDs.count 
																	#'RequiredORGroups' = $RequiredORRecursiveIDs.count
																	#'NotRequiredGroups' = $NotRequiredRecursiveIDs.count
																	#'HasADOUCNRules' = $Policy.HasADOURules
																	#'RequiredORADOUCNs' = $Policy.RequiredOrOUs.count
																	#'NotRequiredADOUCNs' = $Policy.NotRequiredOUs.count																	
																	'ApplyOnlyIfExistingPolicyBlank' = $Policy.ApplyOnlyIfExistingPolicyBlank
																	'Simulation' = !$MakeChanges
																	'ObjectID' = $user.ObjectID
																	'State' = -3
																	'FailedUserToBeRemovedFromLicensingGroup' = $false
																}
															}
														} else {
															$DisabledUsersThisPolicy+=1
															$UserOutput += [PSCustomObject]@{
																'PolicyType' = $Policy.UserType
																'Settings' = $Policy.Settings
																'Default' = $Policy.Default
																'CollectionID' = $Policy.CollectionID
																#'CollectionIDIgnoreThreshold' = $Policy.CollectionIDIgnoreThreshold
																'DisplayName' = $user.DisplayName
																'EmailAddress' = $user.EmailAddress
																'Status' = "User Is Not Enabled"
																'IsLicensed' = $UserIsLicensed
																'Enabled' = $ADObject.AccountEnabled 
																'Policy' = $Policy.ActionType
																'ExistingPolicy' =  $UserPolicyContent
																'RequiredPolicy' = $Policy.ActionPolicy
																'PolicyDescription' = $Policy.Description
																#'PolicyFilter' = $Policy.ActionFilter
																#'FilterTypeSource' = $FilterTypeSource
																#'CSorADOnlineUsers' = $OnlineUsers.Count
																#'HasGroupRules' = $Policy.HasGroupRules
																#'RequiredGroups' = $RequiredRecursiveIDs.count 
																#'RequiredORGroups' = $RequiredORRecursiveIDs.count
																#'NotRequiredGroups' = $NotRequiredRecursiveIDs.count
																#'HasADOUCNRules' = $Policy.HasADOURules
																#'RequiredORADOUCNs' = $Policy.RequiredOrOUs.count
																#'NotRequiredADOUCNs' = $Policy.NotRequiredOUs.count																
																'ApplyOnlyIfExistingPolicyBlank' = $Policy.ApplyOnlyIfExistingPolicyBlank
																'Simulation' = !$MakeChanges
																'ObjectID' = $user.ObjectID
																'State' = -2
																'FailedUserToBeRemovedFromLicensingGroup' = $RemoveLGForDisabledUsers
															}
															
															if ( ($Policy.OnFailureApplytoAllUsersOnNextRun -band 4) -eq 4 -or ($GlobalOnFailureApplytoAllUsersOnNextRun -band 2) -eq 2) {
																if ($Policy.UserType -eq 'Teacher') {
																	$ApplytoAllTeachersOnNextRunThisFunc+=1
																} else {
																	$ApplytoAllStudentsOnNextRunThisFunc+=1
																}
															}
															
															
															$DisabledUsersErrorsCaptured+=1
															if ($DisabledUsersErrorsCaptured -le $RepeatLogEventThreshold) {
																write-host "Cannot Apply Policy to User - User is Disabled - CollectionID: $PadCollectionID   $PadSettings   Policy Type: $PadActionType   Policy Name: $PadActionPolicy   User Email: $($user.EmailAddress)" -ForegroundColor Red
															}
		#													if ($DetailedLoggingEnabled) {
		#														Add-Content -Path "$LogFile" -Value "Cannot apply Policy to user - user is Disabled - CollectionID: $PadCollectionID   $PadSettings   Policy Type: $PadActionType   Policy Name: $PadActionPolicy   User Email: $($user.EmailAddress)" -Encoding Unicode
		#													}
															if ($DisabledUsersErrorsCaptured -eq $RepeatLogEventThreshold) {
																write-host "RepeatLogEventThereshold: Further messages about Disabled users will be supressed in Policy Processing" -ForegroundColor yellow
															}
														}

													} else {
														#user not to be processed due to policy correct or not blank
														if ($Policy.ApplyOnlyIfExistingPolicyBlank -eq $true) {
															$string = 'User has the required Policy or Existing Policy is not Blank'
														} else {
															$string = 'User has the required Policy'
														}
														$UserOutput += [PSCustomObject]@{
															'PolicyType' = $Policy.UserType
															'Settings' = $Policy.Settings
															'Default' = $Policy.Default
															'CollectionID' = $Policy.CollectionID
															#'CollectionIDIgnoreThreshold' = $Policy.CollectionIDIgnoreThreshold
															'DisplayName' = $user.DisplayName
															'EmailAddress' = $user.EmailAddress
															'Status' = $string
															'IsLicensed' = $UserIsLicensed
															'Enabled' = $ADObject.AccountEnabled 
															'Policy' = $Policy.ActionType
															'ExistingPolicy' =  $UserPolicyContent
															'RequiredPolicy' = $Policy.ActionPolicy
															'PolicyDescription' = $Policy.Description
															#'PolicyFilter' = $Policy.ActionFilter
															#'FilterTypeSource' = $FilterTypeSource
															#'CSorADOnlineUsers' = $OnlineUsers.Count
															#'HasGroupRules' = $Policy.HasGroupRules
															#'RequiredGroups' = $RequiredRecursiveIDs.count 
															#'RequiredORGroups' = $RequiredORRecursiveIDs.count
															#'NotRequiredGroups' = $NotRequiredRecursiveIDs.count
															#'HasADOUCNRules' = $Policy.HasADOURules
															#'RequiredORADOUCNs' = $Policy.RequiredOrOUs.count
															#'NotRequiredADOUCNs' = $Policy.NotRequiredOUs.count															
															'ApplyOnlyIfExistingPolicyBlank' = $Policy.ApplyOnlyIfExistingPolicyBlank
															'Simulation' = !$MakeChanges
															'ObjectID' = $user.ObjectID
															'State' = if ($UserPolicyContent -eq $Policy.ActionPolicy) { 3 } else { 0 }
															'FailedUserToBeRemovedFromLicensingGroup' = $false
														}
													}

												} else {
													
													$UserOutput += [PSCustomObject]@{
														'PolicyType' = $Policy.UserType
														'Settings' = $Policy.Settings
														'Default' = $Policy.Default
														'CollectionID' = $Policy.CollectionID
														#'CollectionIDIgnoreThreshold' = $Policy.CollectionIDIgnoreThreshold
														'DisplayName' = $user.DisplayName
														'EmailAddress' = $user.EmailAddress
														'Status' = "User Filtered by AD OU/CN Rules"
														'IsLicensed' = $UserIsLicensed
														'Enabled' = $ADObject.AccountEnabled 
														'Policy' = $Policy.ActionType
														'ExistingPolicy' =  $UserPolicyContent
														'RequiredPolicy' = $Policy.ActionPolicy
														'PolicyDescription' = $Policy.Description
														#'PolicyFilter' = $Policy.ActionFilter
														#'FilterTypeSource' = $FilterTypeSource
														#'CSorADOnlineUsers' = $OnlineUsers.Count
														#'HasGroupRules' = $Policy.HasGroupRules
														#'RequiredGroups' = $RequiredRecursiveIDs.count 
														#'RequiredORGroups' = $RequiredORRecursiveIDs.count
														#'NotRequiredGroups' = $NotRequiredRecursiveIDs.count
														#'HasADOUCNRules' = $Policy.HasADOURules
														#'RequiredORADOUCNs' = $Policy.RequiredOrOUs.count
														#'NotRequiredADOUCNs' = $Policy.NotRequiredOUs.count														
														'ApplyOnlyIfExistingPolicyBlank' = $Policy.ApplyOnlyIfExistingPolicyBlank
														'Simulation' = !$MakeChanges
														'ObjectID' = $user.ObjectID
														'State' = 0
														'FailedUserToBeRemovedFromLicensingGroup' = $false
													}												

												}







											} else {
												#user not found online, even though some filtered users were - user likely was filtered out
												if ($Filter) {
													$string = "User Missing in Filtered $FilterType $FilterSource"
													$stringb = "(user filtered)"
												} else {
													$string = "User Missing in $FilterType $FilterSource"
													$stringb = "(user missing)"
												}
												$ADObject = $AllAzureADUsers | where { $_.ObjectID -eq $user.ObjectID } | select -first 1
										
												$UserOutput += [PSCustomObject]@{
													'PolicyType' = $Policy.UserType
													'Settings' = $Policy.Settings
													'Default' = $Policy.Default
													'CollectionID' = $Policy.CollectionID
													#'CollectionIDIgnoreThreshold' = $Policy.CollectionIDIgnoreThreshold
													'DisplayName' = $user.DisplayName
													'EmailAddress' = $user.EmailAddress
													'Status' = $string
													'IsLicensed' = $UserIsLicensed
													'Enabled' = $ADObject.AccountEnabled   
													'Policy' = $Policy.ActionType
													'ExistingPolicy' = $stringb
													'RequiredPolicy' = $Policy.ActionPolicy
													'PolicyDescription' = $Policy.Description
													#'PolicyFilter' = $Policy.ActionFilter
													#'FilterTypeSource' = $FilterTypeSource
													#'CSorADOnlineUsers' = $OnlineUsers.Count
													#'HasGroupRules' = $Policy.HasGroupRules
													#'RequiredGroups' = $RequiredRecursiveIDs.count 
													#'RequiredORGroups' = $RequiredORRecursiveIDs.count
													#'NotRequiredGroups' = $NotRequiredRecursiveIDs.count
													#'HasADOUCNRules' = $Policy.HasADOURules
													#'RequiredORADOUCNs' = $Policy.RequiredOrOUs.count
													#'NotRequiredADOUCNs' = $Policy.NotRequiredOUs.count													
													'ApplyOnlyIfExistingPolicyBlank' = $Policy.ApplyOnlyIfExistingPolicyBlank
													'Simulation' = !$MakeChanges
													'ObjectID' = $user.ObjectID
													'State' = 0
													'FailedUserToBeRemovedFromLicensingGroup' = $false
												}
											}
										} else {
											$ADObject = $AllAzureADUsers | where { $_.ObjectID -eq $user.ObjectID } | select -first 1 # 1.0.94
											$UserOutput += [PSCustomObject]@{
												'PolicyType' = $Policy.UserType
												'Settings' = $Policy.Settings
												'Default' = $Policy.Default
												'CollectionID' = $Policy.CollectionID
												#'CollectionIDIgnoreThreshold' = $Policy.CollectionIDIgnoreThreshold
												'DisplayName' = $user.DisplayName
												'EmailAddress' = $user.EmailAddress
												'Status' = "User Filtered by Group Rules"
												'IsLicensed' = $UserIsLicensed
												'Enabled' = $ADObject.AccountEnabled # 1.0.94
												'Policy' = $Policy.ActionType
												'ExistingPolicy' =  ''
												'RequiredPolicy' = $Policy.ActionPolicy
												'PolicyDescription' = $Policy.Description
												#'PolicyFilter' = $Policy.ActionFilter
												#'FilterTypeSource' = $FilterTypeSource
												#'CSorADOnlineUsers' = $OnlineUsers.Count
												#'HasGroupRules' = $Policy.HasGroupRules
												#'RequiredGroups' = $RequiredRecursiveIDs.count 
												#'RequiredORGroups' = $RequiredORRecursiveIDs.count
												#'NotRequiredGroups' = $NotRequiredRecursiveIDs.count
												#'HasADOUCNRules' = $Policy.HasADOURules
												#'RequiredORADOUCNs' = $Policy.RequiredOrOUs.count
												#'NotRequiredADOUCNs' = $Policy.NotRequiredOUs.count												
												'ApplyOnlyIfExistingPolicyBlank' = $Policy.ApplyOnlyIfExistingPolicyBlank
												'Simulation' = !$MakeChanges
												'ObjectID' = $user.ObjectID
												'State' = 0
												'FailedUserToBeRemovedFromLicensingGroup' = $false
											}									
										}
									
									} else {
										$DisabledUsersThisPolicy+=1 # if they are not licensed, they cant be enabled
										$UserOutput += [PSCustomObject]@{
											'PolicyType' = $Policy.UserType
											'Settings' = $Policy.Settings
											'Default' = $Policy.Default
											'CollectionID' = $Policy.CollectionID
											#'CollectionIDIgnoreThreshold' = $Policy.CollectionIDIgnoreThreshold
											'DisplayName' = $user.DisplayName
											'EmailAddress' = $user.EmailAddress
											'Status' = "User is Unlicensed"
											'IsLicensed' = $UserIsLicensed
											'Enabled' = ''   
											'Policy' = $Policy.ActionType
											'ExistingPolicy' = ''
											'RequiredPolicy' = $Policy.ActionPolicy
											'PolicyDescription' = $Policy.Description
											#'PolicyFilter' = $Policy.ActionFilter
											#'FilterTypeSource' = $FilterTypeSource
											#'CSorADOnlineUsers' = $OnlineUsers.Count
											#'HasGroupRules' = $Policy.HasGroupRules
											#'RequiredGroups' = $RequiredRecursiveIDs.count 
											#'RequiredORGroups' = $RequiredORRecursiveIDs.count
											#'NotRequiredGroups' = $NotRequiredRecursiveIDs.count
											#'HasADOUCNRules' = $Policy.HasADOURules
											#'RequiredORADOUCNs' = $Policy.RequiredOrOUs.count
											#'NotRequiredADOUCNs' = $Policy.NotRequiredOUs.count											
											'ApplyOnlyIfExistingPolicyBlank' = $Policy.ApplyOnlyIfExistingPolicyBlank
											'Simulation' = !$MakeChanges
											'ObjectID' = $user.ObjectID
											'State' = -2
											'FailedUserToBeRemovedFromLicensingGroup' = $RemoveLGForNotLicensedUsers
										}

										if ( ($Policy.OnFailureApplytoAllUsersOnNextRun -band 8) -eq 8 -or ($GlobalOnFailureApplytoAllUsersOnNextRun -band 2) -eq 2) {
											if ($Policy.UserType -eq 'Teacher') {
												$ApplytoAllTeachersOnNextRunThisFunc+=1
											} else {
												$ApplytoAllStudentsOnNextRunThisFunc+=1	
											}
										}
									}

									$ResultCount+=1
									$UsersProcessed+=1

									if ($ResultCount -eq $LongJobUpdateInterval) {
										[int]$ResultCount=0

										$UsersProcessedString, $timestring, $timeremainingseconds = get-timeelapsed -StartTime $funcstarttime -Processed $UsersProcessed -ToProcess ($UsersToProcess.Count)

										if ($timeremainingseconds -ge 10) {
											write-host "$($simstring)Get Existing User Policy - CollectionID: $PadCollectionID   $PadSettings - Processed $UsersProcessedString of $($UsersToProcess.Count) users - ETR$timestring" -ForegroundColor Gray

											if ($DetailedLoggingEnabled) {
												Add-Content -Path "$LogFile" -Value "$($simstring)Get Existing User Policy - CollectionID: $PadCollectionID   $PadSettings - Processed $UsersProcessedString of $($UsersToProcess.Count) users - ETR$timestring" -Encoding Unicode  
											}
										}
									}
								} # end for user loop

								# successful policy invocation - although no users may still have been affected

								#$PadUsersToApply=[string]$UsersToApply.count
								#$PadUsersToApply = $PadUsersToApply.PadRight(4," ")
						
								if ($UsersToApply.count -gt 0) {
									[string]$OperationID = ''
									[string]$OperationName = ''
									#$OperationName = "$DescString $($Policy.ActionType) - $($Policy.ActionPolicy)  STEPER Profile: $ProfileName"
									

									#$string = $UsersToApply[0].EmailAddress # 1.0.53

									if ($Policy.UserType -eq 'Teacher') {
										$TeamsPolicyBatchProcessingThreshold = $TeacherTeamsPolicyBatchProcessingThreshold
									}

									if ($Policy.UserType -eq 'Student') {
										$TeamsPolicyBatchProcessingThreshold = $StudentTeamsPolicyBatchProcessingThreshold
									}

				
									if ($UsersToApply.count -ge $TeamsPolicyBatchProcessingThreshold) {
										
										[int]$BatchSkipFront=0
										[int]$TrackerCount = 0
										$Portion = $null
										$Portion = @()
										do {
											if ($UsersToApply.count -le $MaximumBatchSize) {
													$Portion = @($UsersToApply)
											} else {
												if ($UsersToApply.count -gt 0) {
													$Portion = @($UsersToApply | Select -Last ($UsersToApply.count - $BatchSkipFront) | Select -First $MaximumBatchSize)
												} else {
													$Portion = $UsersToApply
												}
											}
											$TrackerCount++
											
											$PadUsersToApply=[string]$Portion.count
											$PadUsersToApply = $PadUsersToApply.PadRight(4," ")
											$string = $Portion[0].EmailAddress
										
											$OperationName = "$($Policy.CollectionID) - $($Policy.Settings) - $($Policy.ActionType) - $($Policy.ActionPolicy) - STEPER Profile $ProfileName - Batch $TrackerCount"
											if ($MakeChanges -eq $true) {
												$e = $error.count
												$OperationID = New-CsBatchPolicyAssignmentOperation -PolicyType $Policy.ActionType -PolicyName $Policy.ActionPolicy -Identity $Portion.ObjectID -OperationName $OperationName -WarningAction SilentlyContinue # 1.0.84
												
												#write-host "New-CsBatchPolicyAssignmentOperation -PolicyType $Policy.ActionType -PolicyName $Policy.ActionPolicy -Identity $UsersToApply -OperationName $OperationName
											  
												if ($error.count -gt $e) {
													$errorstring = ""
													if ($Error[0].Exception.Message) {
														$errorstring = $Error[0].Exception.Message
													} else {
														$errorstring = "(unknown)"
													}
													
													foreach ($person in $Portion) {
														$AssignmentErrors += [PSCustomObject]@{    
															'PolicyType' = $Policy.UserType
															'BatchJob' = $true
															'UserCount' = 1
															'DisplayName' = $person.DisplayName
															'EmailAddress' = $person.EmailAddress
															'IsLicensed' = $person.IsLicensed
															'ObjectID' = $person.ObjectID  
															'Filter' = $Policy.ActionFilter
															'FilterTypeSource' = $FilterTypeSource 
															'Description' = $Policy.Description
															'Policy' = $Policy.ActionType
															'RequiredPolicy' = $Policy.ActionPolicy
															'SettingsSource' = $Policy.Settings
															'ErrorInstance' = $false
															'Error' = $errorstring
															'FailedUserToBeRemovedFromLicensingGroup' = $RemoveLGForAssignmentErrors
														}
													}

													$AssignmentErrors += [PSCustomObject]@{
														'PolicyType' = $Policy.UserType
														'BatchJob' = $true
														'UserCount' = $Portion.Count
														'DisplayName' = $null
														'EmailAddress' = $null
														'IsLicensed' = $null
														'ObjectID' = $null
														'Filter' = $Policy.ActionFilter
														'FilterTypeSource' = $FilterTypeSource 
														'Description' = $Policy.Description
														'Policy' = $Policy.ActionType
														'RequiredPolicy' = $Policy.ActionPolicy
														'SettingsSource' = $Policy.Settings
														'ErrorInstance' = $true
														'Error' = "Job " + [STRING]$TrackerCount + ": " + $errorstring
														'FailedUserToBeRemovedFromLicensingGroup' = $false
													}
													
													if ( ($Policy.OnFailureApplytoAllUsersOnNextRun -band 2) -eq 2 -or ($GlobalOnFailureApplytoAllUsersOnNextRun -band 2) -eq 2) {
														if ($Policy.UserType -eq 'Teacher') {
															$ApplytoAllTeachersOnNextRunThisFunc+=$Portion.Count
														} else {
															$ApplytoAllStudentsOnNextRunThisFunc+=$Portion.Count
														}
													}

													$PolicyBatchesSubmitted += [PSCustomObject]@{
														'PolicyType' = $Policy.UserType
														'ApplyToAll' = $Policy.ApplyToAll
														'Description' = $Policy.Description
														'Policy' = $Policy.ActionType
														'RequiredPolicy' = $Policy.ActionPolicy
														'SettingsSource' = $Policy.Settings
														'Simulation' = !$MakeChanges
														'BatchNumber' = $TrackerCount
														'UsersInBatch' = $Portion.count
														'TotalUsersInAllBatches' = $UsersToApply.Count
														'OperationName' = $OperationName 
														'OperationID' = '-'
														'IncludingUser' = $string
														'Error' = $errorstring
													}
																						
													write-host "Failed to Submit Batch Job $($TrackerCount) - CollectionID: $PadCollectionID   $PadSettings   Policy Type: $PadActionType   Policy Name: $PadActionPolicy   User Qty: $PadUsersToApply   Error: $errorstring" -ForegroundColor Red
													if ($DetailedLoggingEnabled) {
														Add-Content -Path "$LogFile" -Value "Failed to Submit Batch Job $($TrackerCount) - CollectionID: $PadCollectionID   $PadSettings   Policy Type: $PadActionType   Policy Name: $PadActionPolicy   User Qty: $PadUsersToApply   Error: $errorstring" -Encoding Unicode
													}
												} else {                                      

													$PolicyBatchesSubmitted += [PSCustomObject]@{
														'PolicyType' = $Policy.UserType
														'ApplyToAll' = $Policy.ApplyToAll
														'Description' = $Policy.Description
														'Policy' = $Policy.ActionType
														'RequiredPolicy' = $Policy.ActionPolicy
														'SettingsSource' = $Policy.Settings
														'Simulation' = !$MakeChanges
														'BatchNumber' = $TrackerCount
														'UsersInBatch' = $Portion.count
														'TotalUsersInAllBatches' = $UsersToApply.Count
														'OperationName' = $OperationName 
														'OperationID' = $OperationID 
														'IncludingUser' = $string
														'Error' = '(no error)'
													}
																					 
													if ($Verbose -eq $true) {   
														#write-host "Submitted Policy Batch Job - Type: $PadActionType   Policy Name: $PadActionPolicy   User Qty: $PadUsersToApply   OperationName: $OperationName   BatchID: $OperationID   Including: $string" -ForegroundColor green
														write-host "Submitted Policy Batch Job $TrackerCount - User Qty: $PadUsersToApply   OperationName: $OperationName   BatchID: $OperationID   Including: $string" -ForegroundColor green
													}
													if ($DetailedLoggingEnabled) {
														#Add-Content -Path "$LogFile" -Value "Submitted Policy Batch Job - Type: $PadActionType   Policy Name: $PadActionPolicy   User Qty: $PadUsersToApply   OperationName: $OperationName   BatchID: $OperationID   Including: $string" -Encoding Unicode
														Add-Content -Path "$LogFile" -Value "Submitted Policy Batch Job $TrackerCount - User Qty: $PadUsersToApply   OperationName: $OperationName   BatchID: $OperationID   Including: $string" -Encoding Unicode
													}
												}
											} else {
												$PolicyBatchesSubmitted += [PSCustomObject]@{
													'PolicyType' = $Policy.UserType
													'ApplyToAll' = $Policy.ApplyToAll
													'Description' = $Policy.Description
													'Policy' = $Policy.ActionType
													'RequiredPolicy' = $Policy.ActionPolicy
													'SettingsSource' = $Policy.Settings
													'Simulation' = !$MakeChanges
													'BatchNumber' = $TrackerCount
													'UsersInBatch' = $Portion.count
													'TotalUsersInAllBatches' = $UsersToApply.Count
													'OperationName' = $OperationName 
													'OperationID' = '-'
													'IncludingUser' = $string
													'Error' = '(no error)'
												}
												if ($Verbose -eq $true) {   
													#write-host "Simulation: Submitted Policy Batch Job - Type: $PadActionType   Policy Name: $PadActionPolicy   User Qty: $PadUsersToApply   OperationName: $OperationName   BatchID: -      Including: $string" -ForegroundColor Cyan
													write-host "Simulation: Submitted Policy Batch Job $TrackerCount - User Qty: $PadUsersToApply   OperationName: $OperationName   BatchID: -      Including: $string" -ForegroundColor Cyan
												}
												if ($DetailedLoggingEnabled) {
														#Add-Content -Path "$LogFile" -Value "Simulation: Submitted Policy Batch Job - Type: $PadActionType   Policy Name: $PadActionPolicy   User Qty: $PadUsersToApply   OperationName: $OperationName   BatchID: -      Including: $string" -Encoding Unicode
														Add-Content -Path "$LogFile" -Value "Simulation: Submitted Policy Batch Job $TrackerCount - User Qty: $PadUsersToApply   OperationName: $OperationName   BatchID: -      Including: $string" -Encoding Unicode
												}
											}
											if ($Portion.count -gt 0) {
												$BatchSkipFront=$BatchSkipFront+$Portion.Count
											} else {
												$BatchSkipFront++
											}
										} while ( $BatchSkipFront -lt $UsersToApply.count -and $UsersToApply.Count -gt 0)
										
										if ($TrackerCount -gt 1) {
											write-host "Total Number of Batches: $TrackerCount   Total Number of Users: $($UsersToApply.count)" -ForegroundColor green	
											if ($DetailedLoggingEnabled) {
												Add-Content -Path "$LogFile" -Value "Total Number of Batches: $TrackerCount   Total Number of Users: $($UsersToApply.count)" -Encoding Unicode
											}
										}
											
										# work done
										$Output += [PSCustomObject]@{
											'PolicyType' = $Policy.UserType
											'SettingsSource' = $Policy.Settings
											'Default' = $Policy.Default
											'CollectionID' = $Policy.CollectionID
											#'CollectionIDIgnoreThreshold' = $Policy.CollectionIDIgnoreThreshold
											#'CollectionIDsToExclude' = $Policy.CollectionIDsToExclude
											'ApplyToAll' = $Policy.ApplyToAll
											'SourceUsers' = $UsersToProcess.count
											'TeamsDisabledUsers' = ''
											'Filter' = $Policy.ActionFilter
											'FilterTypeSource' = $FilterTypeSource 
											'CSorADOnlineUsers' = $OnlineUsers.Count
											'HasGroupRules' = $Policy.HasGroupRules
											'RequiredGroups' = $RequiredRecursiveIDs.count 
											'RequiredORGroups' = $RequiredORRecursiveIDs.count
											'NotRequiredGroups' = $NotRequiredRecursiveIDs.count
											'HasADOUCNRules' = $Policy.HasADOURules
											'RequiredORADOUCNs' = $Policy.RequiredOrOUs.count
											'NotRequiredADOUCNs' = $Policy.NotRequiredOUs.count
											'ApplyOnlyIfExistingPolicyBlank' = $Policy.ApplyOnlyIfExistingPolicyBlank
											'OnlyApplytoUnlicensedUsers' = $Policy.OnlyApplyToUnlicensed
											'UsersModified' = $UsersToApply.Count
											'Description' = $Policy.Description
											'Policy' = $Policy.ActionType
											'RequiredPolicy' = $Policy.ActionPolicy
											'Simulation' = !$MakeChanges
											'Status' = "Policy Applied via " + [string]$TrackerCount + " Batches"
											'BatchID' = if ($TrackerCount -gt 1 -and $OperationID ) { "Last: " + $OperationID } else { $OperationID }
											'OperationName' = if ($TrackerCount -gt 1 -and $OperationName) { "Last: " + $OperationName } else { $OperationName }
											'AssignmentErrors'=''
										}

									} else {
										$PadUsersToApply=[string]$UsersToApply.count
										$PadUsersToApply = $PadUsersToApply.PadRight(4," ")
										$string = $UsersToApply[0].EmailAddress
										if ($MakeChanges -eq $true) {
											if ($Verbose -eq $true) {   
												write-host "$($simstring)Applying Policies - CollectionID: $PadCollectionID   $PadSettings   Policy Type: $PadActionType   Policy Name: $PadActionPolicy   User Qty: $PadUsersToApply   Including: $string" -ForegroundColor Yellow
											}
											if ($DetailedLoggingEnabled) {
												Add-Content -Path "$LogFile" -Value "$($simstring)Applying Policies - CollectionID: $PadCollectionID   $PadSettings   Policy Type: $PadActionType   Policy Name: $PadActionPolicy   User Qty: $PadUsersToApply   Including: $string" -Encoding Unicode
											}

											[int]$ResultCount=0
											[int]$personprocessed=0
											$funcstarttime = (Get-Date)
											foreach ($person in $UsersToApply) {
												$ResultCount = $ResultCount + 1
												$personprocessed = $personprocessed + 1
												$e = $error.count
												if ($Policy.ActionType -eq 'TeamsAppSetupPolicy') {
													Grant-CsTeamsAppSetupPolicy -PolicyName $Policy.ActionPolicy -Identity $person.ObjectID -WarningAction SilentlyContinue -ErrorAction SilentlyContinue | out-null # 1.0.84
													# adding -ErrorAction SilentlyContinue stops STEPER from being able to catch some errors
												}

												if ($Policy.ActionType -eq 'TeamsAppPermissionPolicy') {
													Grant-CsTeamsAppPermissionPolicy -PolicyName $Policy.ActionPolicy -Identity $person.ObjectID -WarningAction SilentlyContinue -ErrorAction SilentlyContinue | out-null # 1.0.84
												}

												if ($Policy.ActionType -eq 'TeamsCallingPolicy') {
													Grant-CsTeamsCallingPolicy -PolicyName $Policy.ActionPolicy -Identity $person.ObjectID -WarningAction SilentlyContinue -ErrorAction SilentlyContinue | out-null # 1.0.84
												}

												if ($Policy.ActionType -eq 'TeamsChannelsPolicy') {
													Grant-CsTeamsChannelsPolicy -PolicyName $Policy.ActionPolicy -Identity $person.ObjectID -WarningAction SilentlyContinue -ErrorAction SilentlyContinue | out-null # 1.0.84
												}

												if ($Policy.ActionType -eq 'TeamsMeetingBroadcastPolicy') {
													Grant-CsTeamsMeetingBroadcastPolicy -PolicyName $Policy.ActionPolicy -Identity $person.ObjectID -WarningAction SilentlyContinue -ErrorAction SilentlyContinue | out-null # 1.0.84
												}

												if ($Policy.ActionType -eq 'TeamsMeetingPolicy') {
													Grant-CsTeamsMeetingPolicy -PolicyName $Policy.ActionPolicy -Identity $person.ObjectID -WarningAction SilentlyContinue -ErrorAction SilentlyContinue | out-null # 1.0.84
												}

												if ($Policy.ActionType -eq 'TeamsMessagingPolicy') {
													Grant-CsTeamsMessagingPolicy -PolicyName $Policy.ActionPolicy -Identity $person.ObjectID -WarningAction SilentlyContinue -ErrorAction SilentlyContinue | out-null # 1.0.84
												}
												if ($Policy.ActionType -eq 'TeamsFeedbackPolicy') {
													Grant-CsTeamsFeedbackPolicy -PolicyName $Policy.ActionPolicy -Identity $person.ObjectID -WarningAction SilentlyContinue -ErrorAction SilentlyContinue | out-null # 1.0.84
												}

												if ($error.count -gt $e) {
													$errorstring = ""
													if ($Error[0].Exception.Message) {
														$errorstring = $Error[0].Exception.Message
													} else {
														$errorstring = "(unknown)"
													}
													$AssignmentErrors += [PSCustomObject]@{    
														'PolicyType' = $Policy.UserType
														'BatchJob' = $false
														'UserCount' = 1
														'DisplayName' = $person.DisplayName
														'EmailAddress' = $person.EmailAddress
														'IsLicensed' = $person.IsLicensed
														'ObjectID' = $person.ObjectID  
														'Filter' = $Policy.ActionFilter
														'FilterTypeSource' = $FilterTypeSource 
														'Description' = $Policy.Description
														'Policy' = $Policy.ActionType
														'RequiredPolicy' = $Policy.ActionPolicy
														'SettingsSource' = $Policy.Settings
														'ErrorInstance' = $true
														'Error' = $errorstring
														'FailedUserToBeRemovedFromLicensingGroup' = $RemoveLGForAssignmentErrors
													}
													
													if ( ($Policy.OnFailureApplytoAllUsersOnNextRun -band 2) -eq 2 -or ($GlobalOnFailureApplytoAllUsersOnNextRun -band 2) -eq 2) {
														if ($Policy.UserType -eq 'Teacher') {
															$ApplytoAllTeachersOnNextRunThisFunc+=1
														} else {
															$ApplytoAllStudentsOnNextRunThisFunc+=1
														}
													}
													
													$AssignmentErrorsThisPolicy+=1
													$AssignmentErrorsCaptured+=1
													if ($AssignmentErrorsCaptured -le $RepeatLogEventThreshold) {
														write-host "Failed to apply Policy to user - CollectionID: $PadCollectionID   $PadSettings   Policy Type: $PadActionType   Policy Name: $PadActionPolicy   User Email: $($person.EmailAddress)   Error: $errorstring" -ForegroundColor Red
													}
													if ($AssignmentErrorsCaptured -eq $RepeatLogEventThreshold) {
														write-host "RepeatLogEventThereshold: Further direct Policy Application errors will be supressed" -ForegroundColor yellow
													}

													if ($DetailedLoggingEnabled) {
														Add-Content -Path "$LogFile" -Value "Failed to apply Policy to user - CollectionID: $PadCollectionID   $PadSettings   Policy Type: $PadActionType   Policy Name: $PadActionPolicy   User Email: $($person.EmailAddress)   Error: $errorstring" -Encoding Unicode
													}
												}

												if ($ResultCount -eq $LongJobUpdateInterval) {

													[int]$ResultCount=0

													$UsersProcessedString, $timestring, $timeremainingseconds = get-timeelapsed -StartTime $funcstarttime -Processed $personprocessed -ToProcess ($UsersToApply.Count)

													if ($timeremainingseconds -ge 10) {
														write-host "$($simstring)Applying Policies - Type: $PadActionType   Policy Name: $PadActionPolicy   Processed $UsersProcessedString of $($UsersToApply.Count) users - ETR$timestring" -ForegroundColor Gray

														if ($DetailedLoggingEnabled) {
															Add-Content -Path "$LogFile" -Value "$($simstring)Applying Policies - Type: $PadActionType   Policy Name: $PadActionPolicy   Processed $UsersProcessedString of $($UsersToApply.Count) users - ETR$timestring" -Encoding Unicode  
														}
													}
												}
											}
										} else {
											if ($Verbose -eq $true) {   
												write-host "$($simstring)Applying Policies - CollectionID: $PadCollectionID   $PadSettings   Policy Type: $PadActionType   Policy Name: $PadActionPolicy   User Qty: $PadUsersToApply   Including: $string" -ForegroundColor Cyan
											}
											if ($DetailedLoggingEnabled) {
													Add-Content -Path "$LogFile" -Value "$($simstring)Applying Policies - CollectionID: $PadCollectionID   $PadSettings   Policy Type: $PadActionType   Policy Name: $PadActionPolicy   User Qty: $PadUsersToApply   Including: $string" -Encoding Unicode
											}
										}
										# work done
										$Output += [PSCustomObject]@{
											'PolicyType' = $Policy.UserType
											'SettingsSource' = $Policy.Settings
											'Default' = $Policy.Default
											'CollectionID' = $Policy.CollectionID
											#'CollectionIDIgnoreThreshold' = $Policy.CollectionIDIgnoreThreshold
											#'CollectionIDsToExclude' = $Policy.CollectionIDsToExclude
											'ApplyToAll' = $Policy.ApplyToAll
											'SourceUsers' = $UsersToProcess.count
											'TeamsDisabledUsers' = $DisabledUsersThisPolicy
											'Filter' = $Policy.ActionFilter
											'FilterTypeSource' = $FilterTypeSource 
											'CSorADOnlineUsers' = $OnlineUsers.Count
											'HasGroupRules' = $Policy.HasGroupRules
											'RequiredGroups' = $RequiredRecursiveIDs.count 
											'RequiredORGroups' = $RequiredORRecursiveIDs.count
											'NotRequiredGroups' = $NotRequiredRecursiveIDs.count
											'HasADOUCNRules' = $Policy.HasADOURules
											'RequiredORADOUCNs' = $Policy.RequiredOrOUs.count
											'NotRequiredADOUCNs' = $Policy.NotRequiredOUs.count
											'ApplyOnlyIfExistingPolicyBlank' = $Policy.ApplyOnlyIfExistingPolicyBlank
											'OnlyApplytoUnlicensedUsers' = $Policy.OnlyApplyToUnlicensed
											'UsersModified' = $UsersToApply.Count
											'Description' = $Policy.Description
											'Policy' = $Policy.ActionType
											'RequiredPolicy' = $Policy.ActionPolicy
											'Simulation' = !$MakeChanges
											'Status' = "Policy Applied Directly"
											'BatchID' = '-'
											'OperationName' = '-'
											'AssignmentErrors'= $AssignmentErrorsThisPolicy
										}
									}
								} else {
									# no work to do
									
									if ($Verbose -eq $true) {   
										write-host "$($simstring)No Policy Changes Required - CollectionID: $PadCollectionID   $PadSettings   Policy Type: $PadActionType   Policy Name: $PadActionPolicy" -ForegroundColor $textcolour
									}
									if ($DetailedLoggingEnabled) {
										Add-Content -Path "$LogFile" -Value "$($simstring)No Policy Changes Required - CollectionID: $PadCollectionID   $PadSettings   Policy Type: $PadActionType   Policy Name: $PadActionPolicy" -Encoding Unicode
									}									
									
									$Output += [PSCustomObject]@{
										'PolicyType' = $Policy.UserType
										'SettingsSource' = $Policy.Settings
										'Default' = $Policy.Default
										'CollectionID' = $Policy.CollectionID
										#'CollectionIDIgnoreThreshold' = $Policy.CollectionIDIgnoreThreshold
										#'CollectionIDsToExclude' = $Policy.CollectionIDsToExclude
										'ApplyToAll' = $Policy.ApplyToAll
										'SourceUsers' = $UsersToProcess.count
										'TeamsDisabledUsers' = $DisabledUsersThisPolicy
										'Filter' = $Policy.ActionFilter
										'FilterTypeSource' = $FilterTypeSource 
										'CSorADOnlineUsers' = $OnlineUsers.Count
										'HasGroupRules' = $Policy.HasGroupRules
										'RequiredGroups' = $RequiredRecursiveIDs.count 
										'RequiredORGroups' = $RequiredORRecursiveIDs.count
										'NotRequiredGroups' = $NotRequiredRecursiveIDs.count
										'HasADOUCNRules' = $Policy.HasADOURules
										'RequiredORADOUCNs' = $Policy.RequiredOrOUs.count
										'NotRequiredADOUCNs' = $Policy.NotRequiredOUs.count
										'ApplyOnlyIfExistingPolicyBlank' = $Policy.ApplyOnlyIfExistingPolicyBlank
										'OnlyApplytoUnlicensedUsers' = $Policy.OnlyApplyToUnlicensed
										'UsersModified' = $UsersToApply.Count
										'Description' = $Policy.Description
										'Policy' = $Policy.ActionType
										'RequiredPolicy' = $Policy.ActionPolicy
										'Simulation' = !$MakeChanges
										'Status' = "No Policy Changes Required"
										'BatchID' = '-'
										'OperationName' = '-'
										'AssignmentErrors'= $AssignmentErrorsThisPolicy
									}
								}
							} else {
								# no online users
								if ($Filter) {
									$string = "No filtered $FilterSource users"
								} else {
									$string = "No $FilterSource users"
								}

								$errorstring = ""
								if ($error.count -gt $e) {
									if ($Error[0].Exception.Message) {
										$errorstring =  " - error message: " + $Error[0].Exception.Message
									}
									$Filter = $null
									$OnlineUsers = @()

									$PadUsersNotProcessed=[string]$UsersToProcess.count
									$PadUsersNotProcessed = $PadUsersNotProcessed.PadRight(4," ")

									write-host "There was an error in retrieving $FilterSource Users - CollectionID: $PadCollectionID   $PadSettings   Policy Type: $PadActionType   Policy Name: $PadActionPolicy   Users Not Processed: $PadUsersNotProcessed" -ForegroundColor Red
									$warnings += "- There was an error in retrieving $FilterSource Users - CollectionID: $PadCollectionID   $PadSettings   Policy Type: $PadActionType   Policy Name: $PadActionPolicy   Users Not Processed: $PadUsersNotProcessed"
									if ($DetailedLoggingEnabled) {
										Add-Content -Path "$LogFile" -Value "There was an error in retrieving $FilterSource Users - CollectionID: $PadCollectionID   $PadSettings   Policy Type: $PadActionType   Policy Name: $PadActionPolicy   Users Not Processed: $PadUsersNotProcessed" -Encoding Unicode
									}
								}

								$Output += [PSCustomObject]@{
									'PolicyType' = $Policy.UserType
									'SettingsSource' = $Policy.Settings
									'Default' = $Policy.Default
									'CollectionID' = $Policy.CollectionID
									#'CollectionIDIgnoreThreshold' = $Policy.CollectionIDIgnoreThreshold
									#'CollectionIDsToExclude' = $Policy.CollectionIDsToExclude
									'ApplyToAll' = $Policy.ApplyToAll
									'SourceUsers' = $UsersToProcess.count
									'TeamsDisabledUsers' = ''
									'Filter' = $Policy.ActionFilter
									'FilterTypeSource' = $FilterTypeSource 
									'CSorADOnlineUsers' = 0
									'HasGroupRules' = $Policy.HasGroupRules
									'RequiredGroups' = $RequiredRecursiveIDs.count 
									'RequiredORGroups' = $RequiredORRecursiveIDs.count
									'NotRequiredGroups' = $NotRequiredRecursiveIDs.count
									'HasADOUCNRules' = $Policy.HasADOURules
									'RequiredORADOUCNs' = $Policy.RequiredOrOUs.count
									'NotRequiredADOUCNs' = $Policy.NotRequiredOUs.count
									'ApplyOnlyIfExistingPolicyBlank' = $Policy.ApplyOnlyIfExistingPolicyBlank
									'OnlyApplytoUnlicensedUsers' = $Policy.OnlyApplyToUnlicensed
									'UsersModified' = 0
									'Description' = $Policy.Description
									'Policy' = $Policy.ActionType
									'RequiredPolicy' = $Policy.ActionPolicy
									'Simulation' = !$MakeChanges
									'Status' = $string + $errorstring
									'BatchID' = '-'
									'OperationName' = '-'
									'AssignmentErrors'=''
								}
							}
						} else {
							#no source users for processing against
							$Output += [PSCustomObject]@{
								'PolicyType' = $Policy.UserType
								'SettingsSource' = $Policy.Settings							
								'Default' = $Policy.Default
								'CollectionID' = $Policy.CollectionID
								#'CollectionIDIgnoreThreshold' = $Policy.CollectionIDIgnoreThreshold
								#'CollectionIDsToExclude' = $Policy.CollectionIDsToExclude
								'ApplyToAll' = $Policy.ApplyToAll
								'SourceUsers' = $UsersToProcess.count
								'TeamsDisabledUsers' = ''
								'Filter' = $Policy.ActionFilter
								'FilterTypeSource' = $FilterTypeSource 
								'CSorADOnlineUsers' = ''
								'HasGroupRules' = $Policy.HasGroupRules
								'RequiredGroups' = $RequiredRecursiveIDs.count 
								'RequiredORGroups' = $RequiredORRecursiveIDs.count
								'NotRequiredGroups' = $NotRequiredRecursiveIDs.count
								'HasADOUCNRules' = $Policy.HasADOURules
								'RequiredORADOUCNs' = $Policy.RequiredOrOUs.count
								'NotRequiredADOUCNs' = $Policy.NotRequiredOUs.count
								'ApplyOnlyIfExistingPolicyBlank' = $Policy.ApplyOnlyIfExistingPolicyBlank
								'OnlyApplytoUnlicensedUsers' = $Policy.OnlyApplyToUnlicensed
								'UsersModified' = 0
								'Description' = $Policy.Description
								'Policy' = $Policy.ActionType
								'RequiredPolicy' = $Policy.ActionPolicy
								'Simulation' = !$MakeChanges
								'Status' = "No Source Users to Process"
								'BatchID' = '-'
								'OperationName' = '-'
								'AssignmentErrors'=''
							}
						}
					} else {
					# policy not found
						$Output += [PSCustomObject]@{
							'PolicyType' = $Policy.UserType
							'SettingsSource' = $Policy.Settings
							'Default' = $Policy.Default
							'CollectionID' = $Policy.CollectionID
							#'CollectionIDIgnoreThreshold' = $Policy.CollectionIDIgnoreThreshold
							#'CollectionIDsToExclude' = $Policy.CollectionIDsToExclude
							'ApplyToAll' = $Policy.ApplyToAll
							'SourceUsers' = $UsersToProcess.count
							'TeamsDisabledUsers' = ''
							'Filter' = $Policy.ActionFilter
							'FilterTypeSource' = $FilterTypeSource
							'CSorADOnlineUsers' = ''
							'HasGroupRules' = $Policy.HasGroupRules
							'RequiredGroups' = $RequiredRecursiveIDs.count 
							'RequiredORGroups' = $RequiredORRecursiveIDs.count
							'NotRequiredGroups' = $NotRequiredRecursiveIDs.count
							'HasADOUCNRules' = $Policy.HasADOURules
							'RequiredORADOUCNs' = $Policy.RequiredOrOUs.count
							'NotRequiredADOUCNs' = $Policy.NotRequiredOUs.count
							'ApplyOnlyIfExistingPolicyBlank' = $Policy.ApplyOnlyIfExistingPolicyBlank
							'OnlyApplytoUnlicensedUsers' = $Policy.OnlyApplyToUnlicensed
							'UsersModified' = 0
							'Description' = $Policy.Description
							'Policy' = $Policy.ActionType
							'RequiredPolicy' = $Policy.ActionPolicy
							'Simulation' = !$MakeChanges
							'Status' = "Policy Not Found"
							'BatchID' = '-'
							'OperationName' = '-'
							'AssignmentErrors'=''
						}

						$PadUsersNotProcessed=[string]$UsersToProcess.count
						$PadUsersNotProcessed = $PadUsersNotProcessed.PadRight(4," ")

						write-host "Policy Not Found - CollectionID: $PadCollectionID   $PadSettings   Policy Type: $PadActionType   Policy Name: $PadActionPolicy   Users Not Processed: $PadUsersNotProcessed" -ForegroundColor Yellow
						$warnings += "- Policy Not Found - CollectionID: $PadCollectionID   $PadSettings   Policy Type: $PadActionType   Policy Name: $PadActionPolicy   Users Not Processed: $PadUsersNotProcessed"
						if ($DetailedLoggingEnabled) {
							Add-Content -Path "$LogFile" -Value "Policy Not Found - CollectionID: $PadCollectionID   $PadSettings   Policy Type: $PadActionType   Policy Name: $PadActionPolicy   Users Not Processed: $PadUsersNotProcessed" -Encoding Unicode
						}
					}

				} else {
				# group filter means no users can be processed
					$Output += [PSCustomObject]@{
						'PolicyType' = $Policy.UserType
						'SettingsSource' = $Policy.Settings
						'Default' = $Policy.Default
						'CollectionID' = $Policy.CollectionID
						#'CollectionIDIgnoreThreshold' = $Policy.CollectionIDIgnoreThreshold
						#'CollectionIDsToExclude' = $Policy.CollectionIDsToExclude
						'ApplyToAll' = $Policy.ApplyToAll
						'SourceUsers' = $UsersToProcess.count
						'TeamsDisabledUsers' = ''
						'Filter' = $Policy.ActionFilter
						'FilterTypeSource' = $FilterTypeSource 
						'CSorADOnlineUsers' = ''
						'HasGroupRules' = $Policy.HasGroupRules
						'RequiredGroups' = $RequiredRecursiveIDs.count 
						'RequiredORGroups' = $RequiredORRecursiveIDs.count
						'NotRequiredGroups' = $NotRequiredRecursiveIDs.count
						'HasADOUCNRules' = $Policy.HasADOURules
						'RequiredORADOUCNs' = $Policy.RequiredOrOUs.count
						'NotRequiredADOUCNs' = $Policy.NotRequiredOUs.count
						'ApplyOnlyIfExistingPolicyBlank' = $Policy.ApplyOnlyIfExistingPolicyBlank
						'OnlyApplytoUnlicensedUsers' = $Policy.OnlyApplyToUnlicensed
						'UsersModified' = 0
						'Description' = $Policy.Description
						'Policy' = $Policy.ActionType
						'RequiredPolicy' = $Policy.ActionPolicy
						'Simulation' = !$MakeChanges
						'Status' = "Group Rules Impossible"
						'BatchID' = '-'
						'OperationName' = '-'
						'AssignmentErrors'=''
					}
					$PadUsersNotProcessed=[string]$UsersToProcess.count
					$PadUsersNotProcessed = $PadUsersNotProcessed.PadRight(4," ")

					write-host "Group Rules Impossible - CollectionID: $PadCollectionID   $PadSettings   Policy Type: $PadActionType   Policy Name: $PadActionPolicy   Users Not Processed: $PadUsersNotProcessed" -ForegroundColor Yellow
					$warnings += "- Group Rules Impossible - CollectionID: $PadCollectionID   $PadSettings   Policy Type: $PadActionType   Policy Name: $PadActionPolicy   Users Not Processed: $PadUsersNotProcessed"
					if ($DetailedLoggingEnabled) {
						Add-Content -Path "$LogFile" -Value "Group Rules Impossible - CollectionID: $PadCollectionID   $PadSettings   Policy Type: $PadActionType   Policy Name: $PadActionPolicy   Users Not Processed: $PadUsersNotProcessed" -Encoding Unicode
					}
				}
			} else {
			# group filter means no users can be processed
				$Output += [PSCustomObject]@{
					'PolicyType' = $Policy.UserType
					'SettingsSource' = $Policy.Settings
					'Default' = $Policy.Default
					'CollectionID' = $Policy.CollectionID
					#'CollectionIDIgnoreThreshold' = $Policy.CollectionIDIgnoreThreshold
					#'CollectionIDsToExclude' = $Policy.CollectionIDsToExclude
					'ApplyToAll' = $Policy.ApplyToAll
					'SourceUsers' = $UsersToProcess.count
					'TeamsDisabledUsers' = ''
					'Filter' = $Policy.ActionFilter
					'FilterTypeSource' = $FilterTypeSource 
					'CSorADOnlineUsers' = ''
					'HasGroupRules' = $Policy.HasGroupRules
					'RequiredGroups' = $RequiredRecursiveIDs.count 
					'RequiredORGroups' = $RequiredORRecursiveIDs.count
					'NotRequiredGroups' = $NotRequiredRecursiveIDs.count
					'HasADOUCNRules' = $Policy.HasADOURules
					'RequiredORADOUCNs' = $Policy.RequiredOrOUs.count
					'NotRequiredADOUCNs' = $Policy.NotRequiredOUs.count
					'ApplyOnlyIfExistingPolicyBlank' = $Policy.ApplyOnlyIfExistingPolicyBlank
					'OnlyApplytoUnlicensedUsers' = $Policy.OnlyApplyToUnlicensed
					'UsersModified' = 0
					'Description' = $Policy.Description
					'Policy' = $Policy.ActionType
					'RequiredPolicy' = $Policy.ActionPolicy
					'Simulation' = !$MakeChanges
					'Status' = "Error retrieving Group Rules Users"
					'BatchID' = '-'
					'OperationName' = '-'
					'AssignmentErrors'=''
				}
				$PadUsersNotProcessed=[string]$UsersToProcess.count
				$PadUsersNotProcessed = $PadUsersNotProcessed.PadRight(4," ")

				write-host "Error retrieving Group Rules Users: CollectionID: $PadCollectionID   $PadSettings   Policy Type: $PadActionType   Policy Name: $PadActionPolicy   Users Not Processed: $PadUsersNotProcessed" -ForegroundColor Yellow
				$warnings += "- Error retrieving Group Rules Users: CollectionID: $PadCollectionID   $PadSettings   Policy Type: $PadActionType   Policy Name: $PadActionPolicy   Users Not Processed: $PadUsersNotProcessed"
				if ($DetailedLoggingEnabled) {
					Add-Content -Path "$LogFile" -Value "Error retrieving Group Rules Users: CollectionID: $PadCollectionID   $PadSettings   Policy Type: $PadActionType   Policy Name: $PadActionPolicy   Users Not Processed: $PadUsersNotProcessed" -Encoding Unicode
				}
			}
        } #end for policy loop      

        $TeacherPoliciesProcessed = @($Output | where { $_.PolicyType -eq 'Teacher' })
        $TeacherQty = ($TeacherPoliciesProcessed.usersmodified | Measure-Object -sum).sum
        $TeacherPolicies = @($PoliciesToProcess | where {$_.UserType -eq 'Teacher' }).count

        $StudentPoliciesProcessed = @($Output | where { $_.PolicyType -eq 'Student' })
        $StudentQty = ($StudentPoliciesProcessed.usersmodified | Measure-Object -sum).sum
        $StudentPolicies = @($PoliciesToProcess | where {$_.UserType -eq 'Student' }).count

        $IssueSummary= @()

        if ($DetailedLoggingEnabled) {
            Add-Content -Path "$LogFile" -Value "`r`nUser Policy Change Results:" -Encoding Unicode
            Add-Content -Path "$LogFile" -Value "---------------------------" -Encoding Unicode
        
            Process-UserCSVOutput ($UserOutput)
            $littleobj = @()
			$littleobj = @($UserOutput | where { $_.Status -eq 'Policy Update Required' })
            Add-Content -Path "$LogFile" -Value "$($SimString)$($UserOutput.count) User Policy results with $($littleobj.count) Total Policy Changes" -Encoding Unicode
            $littleobj = @($littleobj  | Sort-Object -Property ObjectID -Unique) # 1.0.94
            Add-Content -Path "$LogFile" -Value "$($littleobj.count) Unique Users that required Policy Changes`r`n" -Encoding Unicode
			
			$DefaultUserOutput  = @($UserOutput | where { $_.Default -eq $true})
			$littleobj = @($DefaultUserOutput | where { $_.Status -eq 'Policy Update Required'})
            Add-Content -Path "$LogFile" -Value "$($SimString)$($DefaultUserOutput.count) User Policy results with $($littleobj.count) Total Policy Changes under Default Processing" -Encoding Unicode
            $littleobj = @($littleobj  | Sort-Object -Property ObjectID -Unique) # 1.0.94
            Add-Content -Path "$LogFile" -Value "$($littleobj.count) Unique Users that required Policy Changes under Default Processing`r`n" -Encoding Unicode
        }

		$NotEnabled = @($UserOutput | where {  $_.Enabled -ne $true -and $_.IsLicensed -eq $true -and ($_.State -ne 0 -and $_.State -ne 1 ) }) # 1.0.94		
        $NotEnabledUser = @($NotEnabled | Sort-Object -Property ObjectID -Unique) # 1.0.94
        if ($DetailedLoggingEnabled) {
            Add-Content -Path "$LogFile" -Value "`r`nLicensed Users That Are Not Enabled:" -Encoding Unicode
			Add-Content -Path "$LogFile" -Value "------------------------------------" -Encoding Unicode
            if ($NotEnabled.Count -gt 0) {
                #Process-UserCSVOutput ($NotEnabledUser | Select -property * -exclude Policy, ExistingPolicy, RequiredPolicy, PolicyFilter, PolicyDescription, ApplyOnlyIfExistingPolicyBlank, Settings, Status | Sort PolicyType, EmailAddress, FailedUserToBeRemovedFromLicensingGroup)
				Process-UserCSVOutput ($NotEnabledUser | where {$true} -Pipelinevariable UserC | Select -Property *, @{name="PolicyInstances"; expression={ @($NotEnabled | where {$_.ObjectID -eq $UserC.ObjectID}).count }} | Select -property * -exclude Default, CollectionID, Policy, ExistingPolicy, RequiredPolicy, PolicyFilter, FilterTypeSource, CSorADOnlineUsers, HasGroupRules, RequiredGroups, RequiredORGroups, NotRequiredGroups, HasADOUCNRules, RequiredORADOUCNs, NotRequiredADOUCNs, PolicyDescription, ApplyOnlyIfExistingPolicyBlank, Settings, Status, State, FailedUserToBeRemovedFromLicensingGroup | Sort @{Expression = "PolicyType"; Descending = $true}, EmailAddress, FailedUserToBeRemovedFromLicensingGroup) # 1.0.94
                Add-Content -Path "$LogFile" -Value "$($NotEnabled.Count) Licensed Users That Are Not Enabled Error Instances were found during Policy Processing, with $($NotEnabledUser.Count) users affected`r`n" -Encoding Unicode
            } else {
                Add-Content -Path "$LogFile" -Value "(none)`r`n" -Encoding Unicode
            }
        }
        if ($NotEnabled.Count -gt 0) {
            write-host "$($NotEnabled.Count) Licensed Users That Are Not Enabled Error Instances were found during Policy Processing, with $($NotEnabledUser.Count) users affected" -ForegroundColor Red
	        $warnings += "- $($NotEnabled.Count) Licensed Users That Are Not Enabled Error Instances were found during Policy Processing, with $($NotEnabledUser.Count) users affected"

            $IssueSummary += [PSCustomObject]@{
                'IssueType' = 'Licensed Users That Are Not Enabled - Instances'
                'Teacher' =   @($NotEnabled | where { $_.PolicyType -eq 'Teacher'}).count
                'Student' =  @($NotEnabled | where { $_.PolicyType -eq 'Student'}).count
                'Total' = $NotEnabled.Count
            }
            $IssueSummary += [PSCustomObject]@{
                'IssueType' = 'Licensed Users That Are Not Enabled - Users'
                'Teacher' =   @($NotEnabledUser | where { $_.PolicyType -eq 'Teacher'}).count
                'Student' =  @($NotEnabledUser | where { $_.PolicyType -eq 'Student'}).count
                'Total' = $NotEnabledUser.Count
            }
        }


        $NotLicensed = @($UserOutput | where { $_.IsLicensed -ne $true}) #1.0.94
        $NotLicensedUser = @($NotLicensed | Sort-Object -Property ObjectID -Unique) # 1.0.94
        if ($DetailedLoggingEnabled) {
            Add-Content -Path "$LogFile" -Value "`r`nUnlicensed Users:" -Encoding Unicode
			Add-Content -Path "$LogFile" -Value "-----------------" -Encoding Unicode
            if ($NotLicensedUser.Count -gt 0) {
                #Process-UserCSVOutput ($NotLicensedUser | Select -property * -exclude Policy, ExistingPolicy, RequiredPolicy, PolicyFilter, PolicyDescription, ApplyOnlyIfExistingPolicyBlank, Settings, Status | Sort PolicyType, EmailAddress, FailedUserToBeRemovedFromLicensingGroup)
				Process-UserCSVOutput ($NotLicensedUser | where {$true} -Pipelinevariable UserC | Select -Property *, @{name="PolicyInstances"; expression={ @($NotLicensed | where {$_.ObjectID -eq $UserC.ObjectID}).count }} | Select -property * -exclude Default, CollectionID, Policy, ExistingPolicy, RequiredPolicy, PolicyFilter, PolicyDescription, FilterTypeSource, CSorADOnlineUsers, HasGroupRules, RequiredGroups, RequiredORGroups, NotRequiredGroups, HasADOUCNRules, RequiredORADOUCNs, NotRequiredADOUCNs, ApplyOnlyIfExistingPolicyBlank, Settings, Status, State, FailedUserToBeRemovedFromLicensingGroup  | Sort @{Expression = "PolicyType"; Descending = $true}, EmailAddress, FailedUserToBeRemovedFromLicensingGroup) # 1.0.94
                Add-Content -Path "$LogFile" -Value "$($NotLicensed.count) Unlicensed User Error Instances were encountered during Policy Processing, with $($NotLicensedUser.count) users affected`r`n" -Encoding Unicode
            } else {
                Add-Content -Path "$LogFile" -Value "(none)`r`n" -Encoding Unicode
            }
        }
        if ($NotLicensed.Count -gt 0) {
            write-host "$($NotLicensed.count) Unlicensed User Error Instances were encountered during Policy Processing, with $($NotLicensedUser.count) users affected" -ForegroundColor Red
	        $warnings += "- $($NotLicensed.count) Unlicensed User Error Instances were encountered during Policy Processing, with $($NotLicensedUser.count) users affected"

            $IssueSummary += [PSCustomObject]@{
                'IssueType' = 'Unlicensed Users - Instances'
                'Teacher' =   @($NotLicensed | where { $_.PolicyType -eq 'Teacher'}).count
                'Student' =  @($NotLicensed | where { $_.PolicyType -eq 'Student'}).count
                'Total' = $NotLicensed.Count
            }
            $IssueSummary += [PSCustomObject]@{
                'IssueType' = 'Unlicensed Users - Users'
                'Teacher' =   @($NotLicensedUser | where { $_.PolicyType -eq 'Teacher'}).count
                'Student' =  @($NotLicensedUser | where { $_.PolicyType -eq 'Student'}).count
                'Total' = $NotLicensedUser.Count
            }
        }
          

		$ApplyErrorUser = @($AssignmentErrors | Sort-Object -Property ObjectID -Unique) # 1.0.94
        $AssignmentErrors = @($AssignmentErrors | where { $_.ErrorInstance -eq $true})
        if ($DetailedLoggingEnabled) {
	        Add-Content -Path "$LogFile" -Value "`r`nApply Policy Error Instances:" -Encoding Unicode
	        Add-Content -Path "$LogFile" -Value "-----------------------------" -Encoding Unicode
            if ( @($AssignmentErrors).count -gt 0) {
                Process-UserCSVOutput ($AssignmentErrors | Sort EmailAddress, SettingsSource, PolicyType, Policy, RequiredPolicy)
                Add-Content -Path "$LogFile" -Value "$($AssignmentErrors.count) Apply Policy Error Instances were encountered during Policy Processing, with $($ApplyErrorUser.count) users affected`r`n" -Encoding Unicode
            } else {
                Add-Content -Path "$LogFile" -Value "(none)`r`n" -Encoding Unicode
            }
	        Add-Content -Path "$LogFile" -Value "`r`nApply Policy Error Users:" -Encoding Unicode
	        Add-Content -Path "$LogFile" -Value "-------------------------" -Encoding Unicode
            if ( @($ApplyErrorUser).count -gt 0) {
    	        #Process-UserCSVOutput ($ApplyErrorUser | Select -property * -exclude Policy, RequiredPolicy, SettingsSource, Error, Description, Filter, BatchJob, UserCount, ErrorInstance | Sort PolicyType, EmailAddress, FailedUserToBeRemovedFromLicensingGroup)
				Process-UserCSVOutput ($ApplyErrorUser | where {$true} -Pipelinevariable UserC | Select -Property *, @{name="PolicyInstances"; expression={ @($AssignmentErrors | where {$_.ObjectID -eq $UserC.ObjectID}).count -and $_.State -gt -3}} | Select -property * -exclude Policy, RequiredPolicy, SettingsSource, Error, Description, Filter, FilterTypeSource, BatchJob, UserCount, ErrorInstance, State, FailedUserToBeRemovedFromLicensingGroup | Sort @{Expression = "PolicyType"; Descending = $true}, EmailAddress, FailedUserToBeRemovedFromLicensingGroup)
            } else {
                Add-Content -Path "$LogFile" -Value "(none)`r`n" -Encoding Unicode
            }
        }
        if ( @($AssignmentErrors).count -gt 0) {
			write-host "$($AssignmentErrors.count) Apply Policy Error Instances were encountered during Policy Processing, with $($ApplyErrorUser.count) users affected" -ForegroundColor Red
			$warnings += "- $($AssignmentErrors.count) Apply Policy Error Instances were encountered during Policy Processing, with $($ApplyErrorUser.count) users affected"

            $IssueSummary += [PSCustomObject]@{
                'IssueType' = 'Apply Policy Errors - Instances'
                'Teacher' =   @($AssignmentErrors | where { $_.PolicyType -eq 'Teacher'}).count
                'Student' =  @($AssignmentErrors | where { $_.PolicyType -eq 'Student'}).count
                'Total' = $AssignmentErrors.count
            }

            $IssueSummary += [PSCustomObject]@{
                'IssueType' = 'Apply Policy Error - Users'
                'Teacher' =   @($ApplyErrorUser | where { $_.PolicyType -eq 'Teacher'}).count
                'Student' =  @($ApplyErrorUser | where { $_.PolicyType -eq 'Student'}).count
                'Total' = $ApplyErrorUser.Count
            }
        }

        $UsersToDeLicense+= @($NotEnabledUser | where { $_.FailedUserToBeRemovedFromLicensingGroup -eq $true } | Sort-Object -Property ObjectID -Unique | Select @{N=’UserType’; E={$_.PolicyType}}, EmailAddress, DisplayName, ObjectID, @{N=’Problem’; E={'Not Enabled'}}, @{N=’Source’; E={'Policy Processing'}}, @{N=’Settings’; E={$_.Settings}}, @{N=’CollectionID’; E={$_.CollectionID}}, @{N=’State’; E={$_.State}} ) # 1.0.94
        $UsersToDeLicense+= @($NotLicensedUser | where { $_.FailedUserToBeRemovedFromLicensingGroup -eq $true } | Sort-Object -Property ObjectID -Unique | Select @{N=’UserType’; E={$_.PolicyType}}, EmailAddress, DisplayName, ObjectID, @{N=’Problem’; E={'Not Licensed'}}, @{N=’Source’; E={'Policy Processing'}}, @{N=’Settings’; E={$_.Settings}}, @{N=’CollectionID’; E={$_.CollectionID}}, @{N=’State’; E={$_.State}}) # 1.0.94
        $UsersToDeLicense+= @($ApplyErrorUser | where { $_.FailedUserToBeRemovedFromLicensingGroup -eq $true } | Sort-Object -Property ObjectID -Unique | Select @{N=’UserType’; E={$_.PolicyType}}, EmailAddress, DisplayName, ObjectID, @{N=’Problem’; E={'Assignment Error'}}, @{N=’Source’; E={'Policy Processing'}}, @{N=’Settings’; E={$_.SettingsSource}}, @{N=’CollectionID’; E={$_.CollectionID}}, @{N=’State’; E={$_.State}}) # 1.0.94
        $UsersToDeLicense = @($UsersToDeLicense | Sort-Object -Property ObjectID -Unique) # 1.0.94

        $results += [PSCustomObject]@{
            'Type' = "Policy Processing - $($TeacherPoliciesProcessed.Count) Teacher Policies"
		    'Quantity' = $TeacherQty
		    'Source' = "$TeacherPolicies Policies"
            'Action' = if ($TeacherQty -gt 0) { "$($SimString)Policies Applied or Submitted" } else { "$($SimString)Policies Processed" }
            'Target' = 'Teachers'
            'OtherInfo' = ''
            #'AssignmentErrors' = ($AssignmentErrors | where { $_.PolicyType -eq 'Teacher'  } | Select UserCount | Measure-Object -sum).sum
            'AssignmentErrors' = @($ApplyErrorUser | where { $_.PolicyType -eq 'Teacher'}).count
        }


        $results += [PSCustomObject]@{
            'Type' = "Policy Processing - $($StudentPoliciesProcessed.Count) Student Policies"
		    'Quantity' = $StudentQty
		    'Source' = "$StudentPolicies Policies"
            'Action' = if ($StudentQty -gt 0) { "$($SimString)Policies Applied or Submitted" } else { "$($SimString)Policies Processed" }
            'Target' = 'Students'
            'OtherInfo' = ''
            #'AssignmentErrors' =  ($AssignmentErrors | where { $_.PolicyType -eq 'Student'  } | Select UserCount | Measure-Object -sum).sum
            'AssignmentErrors' =  @($ApplyErrorUser | where { $_.PolicyType -eq 'Student'}).count
        }

        if ($DetailedLoggingEnabled) {
			
		if ($iniFile.Misc.GetExtraStatistics -ne $false -and $iniFile.Misc.GetExtraStatistics -ne 0 ) {
				write-host "Getting Extra Policy Statistics (this may take a few minutes)" -foregroundcolor green 
				Add-Content -Path "$LogFile" -Value "`r`nUser Policy Summary:" -Encoding Unicode
				Add-Content -Path "$LogFile" -Value  "--------------------" -Encoding Unicode
				
				$obj = @($UserOutput  | Sort-Object -Property ObjectID -Unique) # 1.0.94

				$UserSummary = @()
				$PolicySummary = @()
				$PolicySummaryInstance = @()
				
				[int]$ResultCount=0
				[int]$personprocessed=0
				$funcstarttime = (Get-Date)
				foreach ($littleobj in $obj) {
					$ResultCount+=1
					$personprocessed = $personprocessed + 1
					$PolicySummaryInstance = @($UserOutput | where {$_.ObjectID -eq $littleobj.ObjectID})
					$UserSummary+= [PSCustomObject]@{
						'PolicyType' = $littleobj.PolicyType
						'DisplayName' = $littleobj.DisplayName
						'EmailAddress' = $littleobj.EmailAddress
						'ObjectID' = $littleobj.ObjectID
						'IsLicensed' = $littleobj.IsLicensed
						'Enabled' = $littleobj.Enabled
						'PoliciesCorrect' = @($PolicySummaryInstance | where {  $_.State -eq 3}).count
						'PoliciesUpdated' = @($PolicySummaryInstance | where { $_.State -eq 2}).count
						'Group, AD OU/CN or Attribute Filtered' = @($PolicySummaryInstance | where { ($_.State -eq 0 -or $_.State -eq 1 )}).count
						'FilteredByCollectionID' = @($PolicySummaryInstance | where { $_.State -eq -3}).count
						'DisabledorNotLicensed' = @($PolicySummaryInstance | where { ($_.State -eq -2 -or $_.State -eq -1 )}).count
						'PendingRemediation' = if ( $UsersToDeLicense.count -gt 0 ) {  if ( @($UsersToDeLicense | where { $_.ObjectID -eq $littleobj.ObjectID}).count -gt 0) { $true } else {$false} } else { '-' }
					}				
				
					if ($ResultCount -eq $LongJobUpdateInterval) {

						[int]$ResultCount=0

						$UsersProcessedString, $timestring, $timeremainingseconds = get-timeelapsed -StartTime $funcstarttime -Processed $personprocessed -ToProcess ($obj.Count)

						if ($timeremainingseconds -ge 10) {
							write-host "Getting Extra Policy Statistics / User Policy Summary - Processed $UsersProcessedString of $($obj.Count) source users - ETR$timestring" -ForegroundColor Gray
							if ($DetailedLoggingEnabled) {
								Add-Content -Path "$LogFile" -Value "Getting Extra Policy Statistics / User Policy Summary - Processed $UsersProcessedString of $($obj.Count) source users - ETR$timestring" -Encoding Unicode  
							}
						}
					}
				}

				Process-UserCSVOutput ($UserSummary | Sort @{Expression = "PolicyType"; Descending = $true}, EmailAddress)

				#$obj = @($UserOutput  | Sort-Object -Property ObjectID -Unique | Select PolicyType, DisplayName, EmailAddress, ObjectID, IsLicensed, Enabled) # 1.0.94
				#$UserSummary = @($obj | where {$true} -Pipelinevariable UserC | Select -Property *, @{name="PoliciesCorrect"; expression={ @($UserOutput | where {$_.ObjectID -eq $UserC.ObjectID -and $_.State -eq 3}).count }}, @{name="PoliciesUpdated"; expression={ @($UserOutput | where {$_.ObjectID -eq $UserC.ObjectID -and $_.State -eq 2}).count }}, @{name="Group, AD OU/CN or Attribute Filtered"; expression={ @($UserOutput | where {$_.ObjectID -eq $UserC.ObjectID -and ($_.State -eq 0 -or $_.State -eq 1 )}).count }},  @{name="FilteredByCollectionID"; expression={ @($UserOutput | where {$_.ObjectID -eq $UserC.ObjectID -and $_.State -eq -3}).count }},  @{name="DisabledorNotLicensed"; expression={ @($UserOutput | where {$_.ObjectID -eq $UserC.ObjectID -and ($_.State -eq -2 -or $_.State -eq -1 )}).count }}, @{name="PendingRemediation"; expression={ if ( $UsersToDeLicense.count -gt 0 ) { if ( @($UsersToDeLicense | where { $_.ObjectID -eq $UserC.ObjectID}).count -gt 0) { $true } else {$false} } else { '-' } }} )
				# slow			

				#$UserSummaryB = @($obj | Select  @{name="PolicyType"; expression={ $_.PolicyType }}, @{name="DisplayName"; expression={ $_.DisplayName }}, @{name="EmailAddress"; expression={ $_.EmailAddress }}, @{name="ObjectID"; expression={ $_.ObjectID }}, @{name="IsLicensed"; expression={ $_.IsLicensed }},@{name="Enabled"; expression={ $_.Enabled }}, @{name="PoliciesCorrect"; expression={ $ObjectID = $_.ObjectID; @($UserOutput | where {$_.ObjectID -eq $ObjectID -and $_.State -eq 3}).count }}, @{name="PoliciesUpdated"; expression={ $ObjectID = $_.ObjectID; @($UserOutput | where {$_.ObjectID -eq $ObjectID -and $_.State -eq 2}).count }}, @{name="Group, AD OU/CN or Attribute Filtered"; expression={ $ObjectID = $_.ObjectID; @($UserOutput | where {$_.ObjectID -eq $ObjectID -and ($_.State -eq 0 -or $_.State -eq 1 )}).count }},  @{name="FilteredByCollectionID"; expression={ $ObjectID = $_.ObjectID; @($UserOutput | where {$_.ObjectID -eq $ObjectID -and $_.State -eq -3}).count }},  @{name="DisabledorNotLicensed"; expression={ $ObjectID = $_.ObjectID; @($UserOutput | where {$_.ObjectID -eq $ObjectID -and ($_.State -eq -2 -or $_.State -eq -1 )}).count }}, @{name="PendingRemediation"; expression={ $ObjectID = $_.ObjectID; if ( $UsersToDeLicense.count -gt 0 ) { if ( @($UsersToDeLicense | where { $_.ObjectID -eq $ObjectID}).count -gt 0) { $true } else {$false} } else { '-' } }} )				
				#Process-UserCSVOutput ($UserSummaryB | Sort @{Expression = "PolicyType"; Descending = $true}, EmailAddress)	
				# slow
				
				foreach ($Usertype in @("*","Teacher","Student")) {
					
					if ($UserType -eq '*') {
						Add-Content -Path "$LogFile" -Value "`r`nAll Users Policy Summary:" -Encoding Unicode
						Add-Content -Path "$LogFile" -Value "-------------------------" -Encoding Unicode
					} else {
						Add-Content -Path "$LogFile" -Value "`r`n$Usertype Policy Summary:" -Encoding Unicode
						Add-Content -Path "$LogFile" -Value "-----------------------" -Encoding Unicode
					}
					
					$obj = $null
					$obj = @("TeamsAppPermissionPolicy","TeamsAppSetupPolicy","TeamsCallingPolicy","TeamsChannelsPolicy","TeamsFeedbackPolicy","TeamsMeetingBroadcastPolicy","TeamsMeetingPolicy","TeamsMessagingPolicy")
					$PolicySummary = @()
					$PolicySummaryInstance = @()

					foreach ($littleobj in $obj) {
						$PolicySummaryInstance = @($UserOutput | where {$_.Policy -eq $littleobj -and $_.PolicyType -like "$Usertype"})
						$PolicySummary+= [PSCustomObject]@{
							'ActionType' = $littleobj
							'Number of Policy Requests' = @($PoliciesToProcess | where {$_.ActionType -eq $littleobj -and $_.UserType -like "$Usertype" }).count						
							'PoliciesCorrectInstances' = @($PolicySummaryInstance | where { $_.State -eq 3 } ).count
							'PoliciesCorrectUsers' = @($PolicySummaryInstance | where { $_.State -eq 3 } | Sort-Object -Property ObjectID -Unique ).count # 1.0.94
							'PoliciesUpdatedInstances' = @($PolicySummaryInstance | where { $_.State -eq 2 } ).count
							'PoliciesUpdatedUsers' = @($PolicySummaryInstance | where { $_.State -eq 2 } | Sort-Object -Property ObjectID -Unique ).count # 1.0.94
							'Group, AD OU/CN or Attribute Filtered Instances' = @($PolicySummaryInstance | where { ($_.State -eq 0 -or $_.State -eq 1 )} ).count
							'Group, AD OU/CN or Attribute Filtered Users' = @($PolicySummaryInstance | where { ($_.State -eq 0 -or $_.State -eq 1 )} | Sort-Object -Property ObjectID -Unique ).count # 1.0.94
							'FilteredByCollectionIDInstances' = @($PolicySummaryInstance | where { $_.State -eq -3} ).count
							'FilteredByCollectionIDUsers' = @($PolicySummaryInstance | where { $_.State -eq -3} | Sort-Object -Property ObjectID -Unique ).count # 1.0.94
							'MatchingLicensedUsersThatAreNotEnabledInstances' = @($PolicySummaryInstance | where {  $_.Enabled -ne $true -and $_.IsLicensed -eq $true -and ($_.State -ne 0 -and $_.State -ne 1 ) } ).count # 1.0.94
							'MatchingLicensedUsersThatAreNotEnabledUsers' = @($PolicySummaryInstance | where {  $_.Enabled -ne $true -and $_.IsLicensed -eq $true -and ($_.State -ne 0 -and $_.State -ne 1 )} | Sort-Object -Property ObjectID -Unique ).count # 1.0.94
							'AllLicensedUsersThatAreNotEnabledInstances' = @($PolicySummaryInstance | where {  $_.Enabled -ne $true -and $_.IsLicensed -eq $true } ).count # 1.0.94
							'AllLicensedUsersThatAreNotEnabledUsers' = @($PolicySummaryInstance | where {  $_.Enabled -ne $true -and $_.IsLicensed -eq $true } | Sort-Object -Property ObjectID -Unique ).count # 1.0.94
						}
					}
					$PolicySummaryInstance = @($UserOutput | where { $_.PolicyType -like "$Usertype"})
					$PolicySummary+= [PSCustomObject]@{
						'ActionType' = 'All Policies'
						'Number of Policy Requests' = @($PoliciesToProcess | where {$_.UserType -like "$Usertype" }).count	
						'PoliciesCorrectInstances' = @($PolicySummaryInstance | where { $_.State -eq 3 } ).count
						'PoliciesCorrectUsers' = @($PolicySummaryInstance | where { $_.State -eq 3 } | Sort-Object -Property ObjectID -Unique ).count # 1.0.94
						'PoliciesUpdatedInstances' = @($PolicySummaryInstance | where { $_.State -eq 2 } ).count
						'PoliciesUpdatedUsers' = @($PolicySummaryInstance | where { $_.State -eq 2 } | Sort-Object -Property ObjectID -Unique ).count # 1.0.94
						'Group, AD OU/CN or Attribute Filtered Instances' = @($PolicySummaryInstance | where { ($_.State -eq 0 -or $_.State -eq 1 )} ).count
						'Group, AD OU/CN or Attribute Filtered Users' = @($PolicySummaryInstance | where { ($_.State -eq 0 -or $_.State -eq 1 )} | Sort-Object -Property ObjectID -Unique ).count # 1.0.94
						'FilteredByCollectionIDInstances' = @($PolicySummaryInstance | where { $_.State -eq -3} ).count
						'FilteredByCollectionIDUsers' = @($PolicySummaryInstance | where { $_.State -eq -3} | Sort-Object -Property ObjectID -Unique ).count # 1.0.94
						'MatchingLicensedUsersThatAreNotEnabledInstances' = @($PolicySummaryInstance | where {  $_.Enabled -ne $true -and $_.IsLicensed -eq $true -and ($_.State -ne 0 -and $_.State -ne 1 ) } ).count # 1.0.94
						'MatchingLicensedUsersThatAreNotEnabledUsers' = @($PolicySummaryInstance | where {  $_.Enabled -ne $true -and $_.IsLicensed -eq $true -and ($_.State -ne 0 -and $_.State -ne 1 )} | Sort-Object -Property ObjectID -Unique ).count # 1.0.94
						'AllLicensedUsersThatAreNotEnabledInstances' = @($PolicySummaryInstance | where {  $_.Enabled -ne $true -and $_.IsLicensed -eq $true } ).count # 1.0.94
						'AllLicensedUsersThatAreNotEnabledUsers' = @($PolicySummaryInstance | where {  $_.Enabled -ne $true -and $_.IsLicensed -eq $true } | Sort-Object -Property ObjectID -Unique ).count # 1.0.94
					}
					
					$PolicySummary | ft * -AutoSize | Out-String -Width 4096 | Out-File -filepath "$LogFile" -append -Encoding Unicode
					
					$correct = $null
					$updated = $null
					[int]$correct = $PolicySummary[$PolicySummary.count -1 ].PoliciesCorrectInstances
					[int]$updated = $PolicySummary[$PolicySummary.count -1 ].PoliciesUpdatedInstances
					
					$QuickSummary = @()

					$QuickSummary+= [PSCustomObject]@{
						'UserType' = if ($UserType -eq '*') { "All" } else { "$UserType"}
						'Metric' = 'Instances'
						'Measurement' = 'Total Correct'
						'Quantity' = $correct
					}
					
					$QuickSummary+= [PSCustomObject]@{
						'UserType' = if ($UserType -eq '*') { "All" } else { "$UserType"}
						'Metric' = 'Instances'
						'Measurement' = 'Total Updated'
						'Quantity' = $updated
					}					
					
					$QuickSummary+= [PSCustomObject]@{
						'UserType' = if ($UserType -eq '*') { "All" } else { "$UserType"}
						'Metric' = 'Instances'
						'Measurement' = 'Total Correct and Updated Instances'
						'Quantity' = $correct + $updated
					}

					$correct = $null
					$updated = $null
					[int]$correct = $PolicySummary[$PolicySummary.count -1 ].PoliciesCorrectUsers
					[int]$updated = $PolicySummary[$PolicySummary.count -1 ].PoliciesUpdatedUsers
					
					$QuickSummary+= [PSCustomObject]@{
						'UserType' = if ($UserType -eq '*') { "All" } else { "$UserType"}
						'Metric' = 'Users'
						'Measurement' = 'Total Correct'
						'Quantity' = $correct
					}
					
					$QuickSummary+= [PSCustomObject]@{
						'UserType' = if ($UserType -eq '*') { "All" } else { "$UserType"}
						'Metric' = 'Users'
						'Measurement' = 'Total Updated'
						'Quantity' = $updated
					}					
					
					$QuickSummary+= [PSCustomObject]@{
						'UserType' = if ($UserType -eq '*') { "All" } else { "$UserType"}
						'Metric' = 'Users'
						'Measurement' = 'Total Correct and Updated Users'
						'Quantity' = $correct + $updated
					}
					$QuickSummary | ft * | Out-String -Width 4096 | Out-File -filepath "$LogFile" -append -Encoding Unicode
				}
			}
			
            Add-Content -Path "$LogFile" -Value "`r`nPolicy Results:" -Encoding Unicode
            Add-Content -Path "$LogFile" -Value "---------------" -Encoding Unicode
            if ( @($Output).count -gt 0) {
                $Output | ft * -AutoSize | Out-String -Width 4096 | Out-File -filepath "$LogFile" -append -Encoding Unicode
            } else {
                Add-Content -Path "$LogFile" -Value "(none)`r`n" -Encoding Unicode
            }
            Add-Content -Path "$LogFile" -Value "$($SimString)$($Output.Count) Policy Requests Processed with $TeacherQty Teacher Policy Changes and $StudentQty Student Policy Changes`r`n" -Encoding Unicode   
        }

        if ($DetailedLoggingEnabled) {
            Add-Content -Path "$LogFile" -Value "`r`nPolicy Issue Summary:" -Encoding Unicode
            Add-Content -Path "$LogFile" -Value "---------------------" -Encoding Unicode
            if ( @($IssueSummary).count -gt 0) {
				
				$TotalUserIssues = @()
				$TotalUserIssues = @(@($NotEnabledUser | Select PolicyType, ObjectID) + @($NotLicensedUser | Select PolicyType, ObjectID) + @($ApplyErrorUser | Select PolicyType, ObjectID) | Sort-Object -Property ObjectID -Unique) # 1.0.94

				$IssueSummary += [PSCustomObject]@{
					'IssueType' = 'TOTAL INSTANCES'
					'Teacher' =   ($IssueSummary | where { $_.IssueType -like "* - Instances" } | Select -Expand Teacher | Measure-Object -sum).sum
					'Student' =  ($IssueSummary | where { $_.IssueType -like "* - Instances" } | Select -Expand Student | Measure-Object -sum).sum
					'Total' = ($IssueSummary | where { $_.IssueType -like "* - Instances" } | Select -Expand Total | Measure-Object -sum).sum
				}

				$IssueSummary += [PSCustomObject]@{
					'IssueType' = 'TOTAL USERS'
					'Teacher' =   ($IssueSummary | where { $_.IssueType -like "* - Users" } | Select -Expand Teacher | Measure-Object -sum).sum
					'Student' =  ($IssueSummary | where { $_.IssueType -like "* - Users" } | Select -Expand Student | Measure-Object -sum).sum
					'Total' = ($IssueSummary | where { $_.IssueType -like "* - Users" } | Select -Expand Total | Measure-Object -sum).sum
				}
                $IssueSummary += [PSCustomObject]@{
                    'IssueType' = 'TOTAL UNIQUE USERS'
                    'Teacher' =   @($TotalUserIssues | where { $_.PolicyType -eq 'Teacher'}).count
                    'Student' =  @($TotalUserIssues | where { $_.PolicyType -eq 'Student'}).count
                    'Total' = $TotalUserIssues.Count
                }				
				
                $IssueSummary | ft * -AutoSize | Out-String -Width 4096 | Out-File -filepath "$LogFile" -append -Encoding Unicode
            } else {
                Add-Content -Path "$LogFile" -Value "(none)`r`n" -Encoding Unicode
            }
        }

        if ($DetailedLoggingEnabled) {
		    Add-Content -Path "$LogFile" -Value "`r`nPolicy Batch Submission Summary:" -Encoding Unicode
            Add-Content -Path "$LogFile" -Value "--------------------------------" -Encoding Unicode	
		
			if ($PolicyBatchesSubmitted.count -gt 0) {
				        $GoodBatches = @($PolicyBatchesSubmitted | where { $_.Error -eq '(no error)' })
						$BadBatches = @($PolicyBatchesSubmitted | where { $_.Error -ne '(no error)' })
						$GoodBatchChanges = ($GoodBatches.UsersInBatch | Measure-Object -sum).sum
						$BadBatchChanges = ($BadBatches.UsersInBatch | Measure-Object -sum).sum
				
				
				$PolicyBatchesSubmitted | ft * -AutoSize | Out-String -Width 4096 | Out-File -filepath "$LogFile" -append -Encoding Unicode
				Add-Content -Path "$LogFile" -Value "$($SimString)$($GoodBatches.Count) Policy Batch Jobs sucessfully submitted containing $GoodBatchChanges user modifications" -Encoding Unicode
				Add-Content -Path "$LogFile" -Value "$($SimString)$($BadBatches.Count) Policy Batch Jobs failed to be submitted containing $BadBatchChanges user modifications`r`n" -Encoding Unicode
			} else {
				Add-Content -Path "$LogFile" -Value "(none)`r`n" -Encoding Unicode	
			}
		}


        if ($DetailedLoggingEnabled) {
            Add-Content -Path "$LogFile" -Value "`r`n" -Encoding Unicode
        }

        $Actions += "- $($SimString)$($Output.Count) Policy Requests Processed with $TeacherQty Teacher Policy Changes and $StudentQty Student Policy Changes"
        write-host "$($SimString)$($Output.Count) Policy Requests Processed with $TeacherQty Teacher Policy Changes and $StudentQty Student Policy Changes" -ForegroundColor $alttextcolour

		if ($ApplytoAllTeachersOnNextRunThisFunc -gt 0 -or $ApplytoAllStudentsOnNextRunThisFunc -gt 0) {
			$ApplytoAllonNextRunSummary += [PSCustomObject]@{
				'Function' = 'Policy Processing'
				'Teachers' = $ApplytoAllTeachersOnNextRunThisFunc 
				'Students' = $ApplytoAllStudentsOnNextRunThisFunc
			}
		}
		if ($ApplytoAllTeachersOnNextRunThisFunc -gt 0) { $ApplytoAllTeachersOnNextRun=$true }
		if ($ApplytoAllStudentsOnNextRunThisFunc -gt 0) { $ApplytoAllStudentsOnNextRun=$true }


    } else {
        write-host "There are no Filtered Teams Policies to Apply" -foregroundcolor white
        $Actions += "- There are no Filtered Teams Policies to Apply"
        if ($DetailedLoggingEnabled) {
            Add-Content -Path "$LogFile" -Value "`r`nApplying Filtered Teams Policies" -Encoding Unicode
            Add-Content -Path "$LogFile" -Value "================================`r`n" -Encoding Unicode
            Add-Content -Path "$LogFile" -Value "Policies to Process:" -Encoding Unicode
            Add-Content -Path "$LogFile" -Value "--------------------" -Encoding Unicode
            Add-Content -Path "$LogFile" -Value "`r`nThere are no Teams Policies to Apply" -Encoding Unicode
        }
    }
} else {
	if ($DetailedLoggingEnabled) {
		Add-Content -Path "$LogFile" -Value "`r`nFiltered Teams Policies" -Encoding Unicode
		Add-Content -Path "$LogFile" -Value "=======================`r`n" -Encoding Unicode
		Add-Content -Path "$LogFile" -Value "Policies that would be Processed:" -Encoding Unicode
		Add-Content -Path "$LogFile" -Value "---------------------------------" -Encoding Unicode
		if ($PoliciestoProcess.Count -gt 0) {
			$PoliciestoProcess | select -Property * -ExcludeProperty CollectionIDsToExclude | ft * -AutoSize | Out-String -Width 4096 | Out-File -filepath "$LogFile" -append -Encoding Unicode # 1.0.94
			Add-Content -Path "$LogFile" -Value "Note: If the global filter type setting is set to Remote, the filter source is forced to CSOnline`r`n" -Encoding Unicode
		} else {
			Add-Content -Path "$LogFile" -Value "`r`nThere are no Filtered Teams Policies to Apply" -Encoding Unicode
		}
	}	
}


# Information Retrieval - Get Policy Jobs
if ($MajorErrors.Count -eq 0 -and ($iniFile.InformationRetrieval.GetPolicyJobs -eq $true -or $iniFile.InformationRetrieval.GetPolicyJobs -eq 1) ) {
    write-host "Information Retrieval - Get Policy Jobs" -foregroundcolor green
    if ($DetailedLoggingEnabled) {
        Add-Content -Path "$LogFile" -Value "`r`nGet Policy Jobs" -Encoding Unicode
        Add-Content -Path "$LogFile" -Value "===============`r`n" -Encoding Unicode
        Add-Content -Path "$LogFile" -Value "Please note that the search for Policy jobs is tenant wide`r`n" -Encoding Unicode
    }

    $obj = @()
    $e = $error.count
    $obj = get-csbatchpolicyassignmentoperation -WarningAction SilentlyContinue
    if ($error.count -gt $e) {
	    if ($Error[0].Exception.Message) {
		    $errorstring = $Error[0].Exception.Message
	    } else {
		    $errorstring = "(unknown)"
	    }

        $Warnings += "- Get Policy Jobs: An error was encountered trying to get CS Batch Policy Assignment Operation jobs. Error: $errorstring"
        $Actions += "- Get Policy Jobs: Failed to get CS Batch Policy Assignment Operation jobs"
        write-host "Get Policy Jobs: An error was encountered trying to get CS Batch Policy Assignment Operation jobs. Error: $errorstring" -ForegroundColor Red
        if ($DetailedLoggingEnabled) {
            Add-Content -Path "$LogFile" -Value "Get Policy Jobs: An error was encountered trying to get CS Batch Policy Assignment Operation jobs. Error: $errorstring`r`n" -Encoding Unicode      
        }
    } else {
        if ($obj) {
            $Information += "- Get Policy Jobs: Found $($Obj.Count) CS Batch Policy Assignment Operation jobs"
            $Actions += "- Get Policy Jobs: Found $($Obj.Count) CS Batch Policy Assignment Operation jobs"
            write-host "Get Policy Jobs: Found $($Obj.Count) CS Batch Policy Assignment Operation jobs" -ForegroundColor green
            if ($DetailedLoggingEnabled) {
                $obj | ft * -AutoSize | Out-String -Width 4096 | Out-File -filepath "$LogFile" -append -Encoding Unicode
                Add-Content -Path "$LogFile" -Value "Get Policy Jobs: Found $($Obj.Count) CS Batch Policy Assignment Operation jobs`r`n" -Encoding Unicode    
            }
        } else {
            $Information += "- Get Policy Jobs: Could not find any CS Batch Policy Assignment Operation jobs"
            $Actions += "- Get Policy Jobs: Could not find any CS Batch Policy Assignment Operation jobs"
            write-host "Get Policy Jobs: Could not find any CS Batch Policy Assignment Operation jobs" -ForegroundColor green
            if ($DetailedLoggingEnabled) {
                Add-Content -Path "$LogFile" -Value "Get Policy Jobs: Could not find any CS Batch Policy Assignment Operation jobs`r`n" -Encoding Unicode      
            }
        }
    }
}

[int]$ApplytoAllTeachersOnNextRunThisFunc=0
[int]$ApplytoAllStudentsOnNextRunThisFunc=0
if ($MajorErrors.Count -eq 0 -and ($iniFile.LicensingGroups.AssignTeacherstoLicensingGroup -eq $true -or $iniFile.LicensingGroups.AssignTeacherstoLicensingGroup -eq 1 -or $iniFile.LicensingGroups.AssignStudentstoLicensingGroup -eq $true -or $iniFile.LicensingGroups.AssignStudentstoLicensingGroup -eq 1) ) {

    if ($UserstoDelicense.count -gt 0) {
		write-host "User Remediation - Removing Problem users from Licensing Groups" -foregroundcolor green
        if ($DetailedLoggingEnabled) {
            Add-Content -Path "$LogFile" -Value "`r`nUser Remediation - Removing Problem Users from Licensing Groups" -Encoding Unicode
            Add-Content -Path "$LogFile" -Value "===============================================================`r`n" -Encoding Unicode
            Add-Content -Path "$LogFile" -Value "Users to Process:" -Encoding Unicode
            Add-Content -Path "$LogFile" -Value "-----------------" -Encoding Unicode
            Add-Content -Path "$LogFile" -Value "`r`nUser Remediation - Potentially Removing $($UserstoDelicense.count) users from Licensing Groups" -Encoding Unicode
        }
        if ($iniFile.LicensingGroups.AssignTeacherstoLicensingGroup -eq $true -or $iniFile.LicensingGroups.AssignTeacherstoLicensingGroup -eq 1) {
            $userbeingremoved=$null
            $UserRemoval = @()
            $AssignmentErrors = @()
            $AssignmentErrorsCaptured=0
            $UserRemoval = @($UserstoDelicense | where { $_.UserType -eq "Teacher" })
            
            if ($DetailedLoggingEnabled) {
                Add-Content -Path "$LogFile" -Value "`r`nFound $($UserRemoval.count) Teachers for Remediation" -Encoding Unicode
            }
            if ($iniFile.Remediation.OnlyRemediateNewTeachers -ne $false -and $iniFile.Remediation.OnlyRemediateNewTeachers -ne 0) {
                $UserRemoval = @($UserRemoval | where { $TeachersNotInLicensingGroup.ObjectID -contains $_.ObjectID }) # 1.0.84
                if ($DetailedLoggingEnabled) {
                    Add-Content -Path "$LogFile" -Value "Only Remediating New Teachers (those that were not in the Licensing Group): True - $($UserRemoval.count) Teachers selected for Remediation" -Encoding Unicode
                }
				write-host "$($simstring)Only Remediating New Teachers (those that were not in the Licensing Group): True - $($UserRemoval.count) Teachers selected for Remediation" -foregroundcolor $textcolour
            } else {
                if ($DetailedLoggingEnabled) {
                    Add-Content -Path "$LogFile" -Value "Only Remediating New Teachers (those that were not in the Licensing Group): False" -Encoding Unicode
                }
				write-host "$($simstring)Only Remediating New Teachers (those that were not in the Licensing Group): False" -foregroundcolor $textcolour
            }

            if ($UserRemoval.Count -gt 0) {
                $SearchObjectID = $null
                $SearchObjectID = $stafflicensinggroup.ObjectID
                if ($DetailedLoggingEnabled) {
                	Add-Content -Path "$LogFile" -Value "`r`nTeachers Requiring Remediation:" -Encoding Unicode
				    Add-Content -Path "$LogFile" -Value "-------------------------------" -Encoding Unicode
                    Process-UserCSVOutput ($UserRemoval)
                    Add-Content -Path "$LogFile" -Value "`r`nTeacher Search Group Object ID: $SearchObjectID`r`n" -Encoding Unicode
                }
                $stafflicensinggroup = $null # we will only work with the original ObjectID, but need to check its still valid
                $stafflicensinggroup = @(Get-MsolGroup -ObjectID $SearchObjectID)
                if ($stafflicensinggroup.Count -eq 1) {
                    write-host "$($simstring)Teacher Remediation - Removing $($UserRemoval.count) Teachers from the Teacher Licensing Group" -foregroundcolor green        
                    if ($DetailedLoggingEnabled) {
                        Add-Content -Path "$LogFile" -Value "Resulting Group DisplayName: $($stafflicensinggroup.DisplayName)" -Encoding Unicode
			            Add-Content -Path "$LogFile" -Value "Resulting Group ObjectID: $($stafflicensinggroup.objectid)" -Encoding Unicode
                        Add-Content -Path "$LogFile" -Value "Teacher Remediation - Removing $($UserRemoval.count) Teachers from the Teacher Licensing Group" -Encoding Unicode
                    }	
                    $string = $UserRemoval[0].EmailAddress
			        if ($MakeChanges -eq $true) {
				        [int]$ResultCount=0
				        [int]$personprocessed=0
				        $funcstarttime = (Get-Date)

                        foreach ($userbeingremoved in $UserRemoval) {
                            $e = $error.count
                            Remove-MsolGroupMember -GroupObjectId $stafflicensinggroup.objectid -GroupMemberType User -GroupmemberObjectId $userbeingremoved.ObjectID -ErrorAction SilentlyContinue
                            $ResultCount+=1
					        $personprocessed = $personprocessed + 1

					        if ($error.count -gt $e) {
						        $errorstring = ""
						        if ($Error[0].Exception.Message) {
							        $errorstring = $Error[0].Exception.Message
						        } else {
							        $errorstring = "(unknown)"
						        }
						        $AssignmentErrors += [PSCustomObject]@{
						            'DisplayName' = $userbeingremoved.DisplayName
                                    'EmailAddress' = $userbeingremoved.EmailAddress
                                    'UserType' = 'Teacher'						
                                    'ObjectID' = $userbeingremoved.objectid
                                    'GroupObjectID' = $stafflicensinggroup.objectid
						            'Error' = $errorstring
						        }
				                $AssignmentErrorsCaptured+=1				
                                if ($AssignmentErrorsCaptured -le $RepeatLogEventThreshold) {
    						        write-host "Failed to remove Teacher from the Licensing Group $($iniFile.LicensingGroups.AllTeachersLicensingGroupName) - $($userbeingremoved.EmailAddress)" -ForegroundColor Red
                                }
                                if ($AssignmentErrorsCaptured -eq $RepeatLogEventThreshold) {
                                    write-host "RepeatLogEventThereshold: Further Teacher Licensing User Remediation Assignment errors will be supressed" -ForegroundColor yellow
                                }
											
					        }

					        if ($ResultCount -eq $LongJobUpdateInterval) {

						        [int]$ResultCount=0

                                $UsersProcessedString, $timestring, $timeremainingseconds = get-timeelapsed -StartTime $funcstarttime -Processed $personprocessed -ToProcess ($UserRemoval.Count)

						        if ($timeremainingseconds -ge 10) {
							        write-host "Removing Teachers from the Licensing Group - Processed $UsersProcessedString of $($UserRemoval.Count) users - ETR$timestring" -ForegroundColor Gray
							        if ($DetailedLoggingEnabled) {
								        Add-Content -Path "$LogFile" -Value "Removing Teachers from the Licensing Group - Processed $UsersProcessedString of $($UserRemoval.Count) users - ETR$timestring" -Encoding Unicode  
							        }
						        }
					        }	
                        }

                        if ( @($AssignmentErrors).count -gt 0) {
                            if ($DetailedLoggingEnabled) {
				                Add-Content -Path "$LogFile" -Value "`r`nRemove Teachers From Teacher Licensing Group Errors:" -Encoding Unicode
				                Add-Content -Path "$LogFile" -Value "----------------------------------------------------" -Encoding Unicode
				                Process-UserCSVOutput ($AssignmentErrors | Sort DisplayName, EmailAddress)
                                Add-Content -Path "$LogFile" -Value "$($AssignmentErrors.count) Remove from Licensing Group Errors were encountered during Teacher Licensing User Remediation" -Encoding Unicode
                            }
				            write-host "$($AssignmentErrors.count) Remove from Licensing Group Errors were encountered during Teacher Licensing User Remediation" -ForegroundColor Red
				            $warnings += "- $($AssignmentErrors.count) Remove from Licensing Group Errors were encountered during Teacher Licensing User Remediation"
							$ApplytoAllTeachersOnNextRunThisFunc = $AssignmentErrors.count
			            } else {
				            if ($DetailedLoggingEnabled) {
    			                Add-Content -Path "$LogFile" -Value "$($UserRemoval.Count) Teachers were remediated by removal from the Teacher Licensing Group $($iniFile.LicensingGroups.AllTeachersLicensingGroupName), including $string" -Encoding Unicode
			                }
				            $Actions += "- $($UserRemoval.Count) Teachers were remediated by removal from the Teacher Licensing Group $($iniFile.LicensingGroups.AllTeachersLicensingGroupName), including $string"
                            write-host "$($UserRemoval.Count) Teachers were remediated by removal from the Teacher Licensing Group $($iniFile.LicensingGroups.AllTeachersLicensingGroupName), including $string" -ForegroundColor yellow
                        }
                    } else {
    			        if ($DetailedLoggingEnabled) {
                            Add-Content -Path "$LogFile" -Value "Simulation: $($UserRemoval.Count) Teachers would have been remediated by removal from the Teacher Licensing Group $($iniFile.LicensingGroups.AllTeachersLicensingGroupName), including $string" -Encoding Unicode
		                }
			            $Actions += "- Simulation: $($UserRemoval.Count) Teachers would have been remediated by removal from the Teacher Licensing Group $($iniFile.LicensingGroups.AllTeachersLicensingGroupName), including $string"
                        write-host "Simulation: $($UserRemoval.Count) Teachers would have been remediated by removal from the Teacher Licensing Group $($iniFile.LicensingGroups.AllTeachersLicensingGroupName), including $string" -ForegroundColor cyan
			        }
                    $results += [PSCustomObject]@{
                        'Type' = 'Teacher'
				        'Quantity' = $($UserRemoval.Count)
				        'Source' = "$TeacherSourceGroupCount TeacherSourceGroups"
                        'Action' = "$($SimString)Remediate Teachers by Removal from Licensing Group"
                        'Target' = $iniFile.LicensingGroups.AllTeachersLicensingGroupName
                        'OtherInfo' = ''
                        'AssignmentErrors' = $AssignmentErrors.count
                    }
                } else {
                    if ($stafflicensinggroup.Count -gt 1) { # this shouldnt happen as we used the original objectid
                        $Warnings += "- More than one Licensing Group matches the name $($iniFile.LicensingGroups.AllTeachersLicensingGroupName), please check the name. Teacher Licensing User Remediation will not be processed"
                        write-host "More than one Licensing Group matches the name $($iniFile.LicensingGroups.AllTeachersLicensingGroupName), please check the name. Teacher Licensing User Remediation will not be processed" -foreground red
                        if ($DetailedLoggingEnabled) {
                            Add-Content -Path "$LogFile" -Value "More than one Licensing Group matches the name $($iniFile.LicensingGroups.AllTeachersLicensingGroupName), please check the name. Teacher Licensing User Remediation  will not be processed" -Encoding Unicode
                        }
                    } else {
                        $Warnings += "- The Teacher Licensing Group $($iniFile.LicensingGroups.AllTeachersLicensingGroupName) does not exist, Teacher Licensing User Remediation will not be processed"
                        write-host "The Teacher Licensing Group $($iniFile.LicensingGroups.AllTeachersLicensingGroupName) does not exist, Teacher Licensing User Remediation will not be processed" -foreground red
                        if ($DetailedLoggingEnabled) {
                            Add-Content -Path "$LogFile" -Value "The Teacher Licensing Group $($iniFile.LicensingGroups.AllTeachersLicensingGroupName) does not exist, Teacher Licensing User Remediation will not be processed" -Encoding Unicode
                        }
                    }
                }
            } else {
                $Actions += "- Teacher Remediation - There are no Teachers to Remediate by Removal from the Teacher Licensing Group"
                write-host "Teacher Remediation - There are no Teachers to Remediate by Removal from the Teacher Licensing Group" -foreground white
                if ($DetailedLoggingEnabled) {
                    Add-Content -Path "$LogFile" -Value "Teacher Remediation - There are no Teachers to Remediate by Removal from the Teacher Licensing Group" -Encoding Unicode
                }
            }
        }

        if ($iniFile.LicensingGroups.AssignStudentstoLicensingGroup -eq $true -or $iniFile.LicensingGroups.AssignStudentstoLicensingGroup -eq 1) {
            $userbeingremoved=$null
            $UserRemoval = @()
            $AssignmentErrors = @()
            $AssignmentErrorsCaptured=0
            $UserRemoval = @($UserstoDelicense | where { $_.UserType -eq "Student" })

            if ($DetailedLoggingEnabled) {
                Add-Content -Path "$LogFile" -Value "`r`nFound $($UserRemoval.count) Students for Remediation" -Encoding Unicode
            }
            if ($iniFile.Remediation.OnlyRemediateNewStudents -ne $false -and $iniFile.Remediation.OnlyRemediateNewStudents -ne 0) {
                $UserRemoval = @($UserRemoval | where { $StudentsNotInLicensingGroup.ObjectID -contains $_.ObjectID }) # 1.0.84
                if ($DetailedLoggingEnabled) {
                    Add-Content -Path "$LogFile" -Value "Only Remediating New Students (those that were not in the Licensing Group): True - $($UserRemoval.count) Students selected for Remediation" -Encoding Unicode
                }
				write-host "$($simstring)Only Remediating New Students (those that were not in the Licensing Group): True - $($UserRemoval.count) Students selected for Remediation" -foregroundcolor $textcolour
            } else {
                if ($DetailedLoggingEnabled) {
                    Add-Content -Path "$LogFile" -Value "Only Remediating New Students (those that were not in the Licensing Group): False" -Encoding Unicode
                }
				write-host "$($simstring)Only Remediating New Students (those that were not in the Licensing Group): False" -foregroundcolor $textcolour
            }

            if ($UserRemoval.Count -gt 0) {
                $SearchObjectID = $null
                $SearchObjectID = $studentlicensinggroup.ObjectID
                if ($DetailedLoggingEnabled) {
				    Add-Content -Path "$LogFile" -Value "`r`nStudents Requiring Remediation:" -Encoding Unicode
				    Add-Content -Path "$LogFile" -Value "-------------------------------" -Encoding Unicode
                    Process-UserCSVOutput ($UserRemoval)
                    Add-Content -Path "$LogFile" -Value "`r`nStudent Search Group Object ID: $SearchObjectID`r`n" -Encoding Unicode
                }
                $studentlicensinggroup = $null # we will only work with the original ObjectID, but need to check its still valid
                $studentlicensinggroup = @(Get-MsolGroup -ObjectID $SearchObjectID)
                if ($studentlicensinggroup.Count -eq 1) {
                    write-host "$($simstring)Student Remediation - Removing $($UserRemoval.count) Students from the Student Licensing Group" -foregroundcolor green        
                    if ($DetailedLoggingEnabled) {
                        Add-Content -Path "$LogFile" -Value "Resulting Group DisplayName: $($studentlicensinggroup.DisplayName)" -Encoding Unicode
			            Add-Content -Path "$LogFile" -Value "Resulting Group ObjectID: $($studentlicensinggroup.objectid)" -Encoding Unicode
                        Add-Content -Path "$LogFile" -Value "Student Remediation - Removing $($UserRemoval.count) Students from the Student Licensing Group" -Encoding Unicode
                    }	
                    $string = $UserRemoval[0].EmailAddress
			        if ($MakeChanges -eq $true) {
				        [int]$ResultCount=0
				        [int]$personprocessed=0
				        $funcstarttime = (Get-Date)

                        foreach ($userbeingremoved in $UserRemoval) {
                            $e = $error.count
                            Remove-MsolGroupMember -GroupObjectId $studentlicensinggroup.objectid -GroupMemberType User -GroupmemberObjectId $userbeingremoved.ObjectID -ErrorAction SilentlyContinue
                            $ResultCount+=1
					        $personprocessed = $personprocessed + 1

					        if ($error.count -gt $e) {
						        $errorstring = ""
						        if ($Error[0].Exception.Message) {
							        $errorstring = $Error[0].Exception.Message
						        } else {
							        $errorstring = "(unknown)"
						        }
						        $AssignmentErrors += [PSCustomObject]@{
						            'DisplayName' = $userbeingremoved.DisplayName
                                    'EmailAddress' = $userbeingremoved.EmailAddress
                                    'UserType' = 'Student'						
                                    'ObjectID' = $userbeingremoved.objectid
                                    'GroupObjectID' = $studentlicensinggroup.objectid
						            'Error' = $errorstring
						        }
				                $AssignmentErrorsCaptured+=1				
                                if ($AssignmentErrorsCaptured -le $RepeatLogEventThreshold) {
    						        write-host "Failed to remove Student from the Licensing Group $($iniFile.LicensingGroups.AllStudentsLicensingGroupName) - $($userbeingremoved.EmailAddress)" -ForegroundColor Red
                                }
                                if ($AssignmentErrorsCaptured -eq $RepeatLogEventThreshold) {
                                    write-host "RepeatLogEventThereshold: Further Student Licensing User Remediation Assignment errors will be supressed" -ForegroundColor yellow
                                }
											
					        }

					        if ($ResultCount -eq $LongJobUpdateInterval) {

						        [int]$ResultCount=0

                                $UsersProcessedString, $timestring, $timeremainingseconds = get-timeelapsed -StartTime $funcstarttime -Processed $personprocessed -ToProcess ($UserRemoval.Count)

						        if ($timeremainingseconds -ge 10) {
							        write-host "Removing Student from the Licensing Group - Processed $UsersProcessedString of $($UserRemoval.Count) users - ETR$timestring" -ForegroundColor Gray
							        if ($DetailedLoggingEnabled) {
								        Add-Content -Path "$LogFile" -Value "Removing Student from the Licensing Group - Processed $UsersProcessedString of $($UserRemoval.Count) users - ETR$timestring" -Encoding Unicode  
							        }
						        }
					        }	
                        }
                        if ( @($AssignmentErrors).count -gt 0) {
                            if ($DetailedLoggingEnabled) {
				                Add-Content -Path "$LogFile" -Value "`r`nRemove Students From Student Licensing Group Errors:" -Encoding Unicode
				                Add-Content -Path "$LogFile" -Value "----------------------------------------------------" -Encoding Unicode
				                Process-UserCSVOutput ($AssignmentErrors | Sort DisplayName, EmailAddress)
                                Add-Content -Path "$LogFile" -Value "$($AssignmentErrors.count) Remove from Licensing Group Errors were encountered during Student Licensing User Remediation" -Encoding Unicode
                            }
				            write-host "$($AssignmentErrors.count) Remove from Licensing Group Errors were encountered during Student Licensing User Remediation" -ForegroundColor Red
				            $warnings += "- $($AssignmentErrors.count) Remove from Licensing Group Errors were encountered during Student Licensing User Remediation"
							$ApplytoAllStudentsOnNextRunThisFunc  = $AssignmentErrors.count
			            } else {
				            if ($DetailedLoggingEnabled) {
    			                Add-Content -Path "$LogFile" -Value "$($UserRemoval.Count) Students were remediated by removal from the Student Licensing Group $($iniFile.LicensingGroups.AllStudentsLicensingGroupName), including $string" -Encoding Unicode
			                }
				            $Actions += "- $($UserRemoval.Count) Students were remediated by removal from the Student Licensing Group $($iniFile.LicensingGroups.AllStudentsLicensingGroupName), including $string"
                            write-host "$($UserRemoval.Count) Students were remediated by removal from the Student Licensing Group $($iniFile.LicensingGroups.AllStudentsLicensingGroupName), including $string" -ForegroundColor yellow
                        }
                    } else {
    			        if ($DetailedLoggingEnabled) {
                            Add-Content -Path "$LogFile" -Value "Simulation: $($UserRemoval.Count) Students would have been remediated by removal from the Student Licensing Group $($iniFile.LicensingGroups.AllStudentsLicensingGroupName), including $string" -Encoding Unicode
		                }
			            $Actions += "- Simulation: $($UserRemoval.Count) Students would have been remediated by removal from the Student Licensing Group $($iniFile.LicensingGroups.AllStudentsLicensingGroupName), including $string"
                        write-host "Simulation: $($UserRemoval.Count) Students would have been remediated by removal from the Student Licensing Group $($iniFile.LicensingGroups.AllStudentsLicensingGroupName), including $string" -ForegroundColor cyan
			        }
                    $results += [PSCustomObject]@{
                        'Type' = 'Student'
				        'Quantity' = $($UserRemoval.Count)
				        'Source' = "$StudentSourceGroupCount StudentSourceGroups"
                        'Action' = "$($SimString)Remediate Students by Removal from Licensing Group"
                        'Target' = $iniFile.LicensingGroups.AllStudentsLicensingGroupName
                        'OtherInfo' = ''
                        'AssignmentErrors' = $AssignmentErrors.count
                    }
                } else {
                    if ($studentlicensinggroup.Count -gt 1) { # this shouldnt happen as we used the original objectid
                        $Warnings += "- More than one Licensing Group matches the name $($iniFile.LicensingGroups.AllStudentsLicensingGroupName), please check the name. Student Licensing User Remediation will not be processed"
                        write-host "More than one Licensing Group matches the name $($iniFile.LicensingGroups.AllStudentsLicensingGroupName), please check the name. Student Licensing User Remediation will not be processed" -foreground red
                        if ($DetailedLoggingEnabled) {
                            Add-Content -Path "$LogFile" -Value "More than one Licensing Group matches the name $($iniFile.LicensingGroups.AllStudentsLicensingGroupName), please check the name. Student Licensing User Remediation  will not be processed" -Encoding Unicode
                        }
                    } else {
                        $Warnings += "- The Student Licensing Group $($iniFile.LicensingGroups.AllStudentsLicensingGroupName) does not exist, Student Licensing User Remediation will not be processed"
                        write-host "The Student Licensing Group $($iniFile.LicensingGroups.AllStudentsLicensingGroupName) does not exist, Student Licensing User Remediation will not be processed" -foreground red
                        if ($DetailedLoggingEnabled) {
                            Add-Content -Path "$LogFile" -Value "The Student Licensing Group $($iniFile.LicensingGroups.AllStudentsLicensingGroupName) does not exist, Student Licensing User Remediation will not be processed" -Encoding Unicode
                        }
                    }
                }
            } else {
                $Actions += "- Student Remediation - There are no Students to Remediate by Removal from the Student Licensing Group"
                write-host "Student Remediation - There are no Students to Remediate by Removal from the Student Licensing Group" -foreground white
                if ($DetailedLoggingEnabled) {
                    Add-Content -Path "$LogFile" -Value "Student Remediation - There are no Students to Remediate by Removal from the Student Licensing Group" -Encoding Unicode
                }
            }
        }

    } else {
        write-host "User Remediation - There are no Users to Remediate by Removal from Licensing Groups" -foregroundcolor white
        $Actions += "- User Remediation - There are no Users to Remediate by Removal from Licensing Groups"
        if ($DetailedLoggingEnabled) {
            Add-Content -Path "$LogFile" -Value "`r`nUser Problem Resolution - User Remediation - Removing Problem Users from Licensing Groups" -Encoding Unicode
            Add-Content -Path "$LogFile" -Value "=========================================================================================`r`n" -Encoding Unicode
            Add-Content -Path "$LogFile" -Value "Users to Remediate:" -Encoding Unicode
            Add-Content -Path "$LogFile" -Value "-------------------" -Encoding Unicode
            Add-Content -Path "$LogFile" -Value "`r`nThere are no Users to Remediate" -Encoding Unicode
        }
    }
}

#if (!($iniFile.Remediation.OnFailureApplytoAllUsersOnNextRun -eq $true -or $iniFile.Remediation.OnFailureApplytoAllUsersOnNextRun -eq 1 -or $iniFile.Remediation.OnFailureApplytoAllUsersOnNextRun -eq "All" -or $iniFile.Remediation.OnFailureApplytoAllUsersOnNextRun -eq "Error" -or $iniFile.Remediation.OnFailureApplytoAllUsersOnNextRun -eq "Errors" -or $iniFile.Remediation.OnFailureApplytoAllUsersOnNextRun -eq "Assignment" -or $iniFile.Remediation.OnFailureApplytoAllUsersOnNextRun -eq "AssignmentError"  -or $iniFile.Remediation.OnFailureApplytoAllUsersOnNextRun -eq "AssignmentErrors")) { #1.0.85

$Remediation = (@(if ($iniFile.Remediation.OnFailureApplytoAllUsersOnNextRun -ne $null) { if ($iniFile.Remediation.OnFailureApplytoAllUsersOnNextRun.Trim($TrimChar) -ne $null -and $iniFile.Remediation.OnFailureApplytoAllUsersOnNextRun.Trim($TrimChar) -ne "") { @(iex "echo $($iniFile.Remediation.OnFailureApplytoAllUsersOnNextRun.Trim($TrimChar))") | select -Unique | % { switch($_){0 {[int]-99}; "False" {[int]-99}; 1 {[int]14};2 {[int]2}; "A" {[int]2}; "Assignment" {[int]2}; "AssignmentError" {[int]2}; "AssignmentErrors" {[int]2}; "Error" {[int]2}; "Errors" {[int]2};  6 {[int]2,[int]4}; 10 {[int]2,[int]8}; 12 {[int]4,[int]8}; 14 {[int]14}; "All" {[int]14}; "True" {[int]14}; default {[int]0}} } } else { [int]0 } } else { [int]0 } ) | select -Unique | select -first 10 | Measure-Object -sum).sum | % {if ($_ -gt 14) { 14 } else { if ($_ -lt 0) { 0 } else { $_ } }}

if ( ($Remediation -band 2) -ne 2) {	

	if ( ($GlobalOnFailureApplytoAllUsersOnNextRun -band 2) -ne 2) {
		$ApplytoAllTeachersOnNextRunThisFunc=0
		$ApplytoAllStudentsOnNextRunThisFunc=0
	}
}

if ($ApplytoAllTeachersOnNextRunThisFunc -gt 0 -or $ApplytoAllStudentsOnNextRunThisFunc -gt 0) {
	$ApplytoAllonNextRunSummary += [PSCustomObject]@{
		'Function' = 'User Remediation'
		'Teachers' = $ApplytoAllTeachersOnNextRunThisFunc
		'Students' = $ApplytoAllStudentsOnNextRunThisFunc
	}
	if ($ApplytoAllTeachersOnNextRunThisFunc -gt 0) { $ApplytoAllTeachersOnNextRun=$true }
	if ($ApplytoAllStudentsOnNextRunThisFunc -gt 0) { $ApplytoAllStudentsOnNextRun=$true }		
}


#Next Run Overrides

if ($MajorErrors.Count -gt 0 -and ($GlobalOnFailureApplytoAllUsersOnNextRun -band 4) -eq 4) {
	$ApplytoAllonNextRunSummary += [PSCustomObject]@{
		'Function' = "Major Errors in STEPER Processing - $($MajorErrors.Count) errors"
		'Teachers' = 0
		'Students' = 0
	}
	$ApplytoAllTeachersOnNextRun = $true
	$ApplytoAllStudentsOnNextRun = $true 
}

if ($Warnings.Count -gt 0 -and ($GlobalOnFailureApplytoAllUsersOnNextRun -band 8) -eq 8) {
	$ApplytoAllonNextRunSummary += [PSCustomObject]@{
		'Function' = "Warnings in STEPER Processing - $($Warnings.Count) warnings"
		'Teachers' = 0
		'Students' = 0
	}
	$ApplytoAllTeachersOnNextRun = $true
	$ApplytoAllStudentsOnNextRun = $true 
}


if ($regpath) {
	if ($ApplytoAllTeachersOnNextRun -eq $true) {
		Set-RegistryValue -Path $regpath -Name 'ApplytoAllTeachersOnNextRun' -ValueType DWORD -Value 1
		if (($ApplytoAllonNextRunSummary.Teachers | measure-object -sum).sum -gt 0) {
			$Information += "- User Re-Processing: ApplytoAllTeachersOnNextRun requested Due to Failed Users, or Errors or Warnings"
		} else {
			$Information += "- User Re-Processing: ApplytoAllTeachersOnNextRun requested Due to Errors or Warnings"
		}
	} else {
		if ($MajorErrors.Count -eq 0) {	
			if ($MakeChanges -eq $true) {
				Remove-ItemProperty -Path $regpath -Name 'ApplytoAllTeachersOnNextRun' -Force -Erroraction SilentlyContinue
				$Information += "- User Re-Processing: ApplytoAllTeachersOnNextRun not re-requested on next run"
			} else {
				if ((Get-RegistryValue -Path "$regpath" -Name 'ApplytoAllTeachersOnNextRun' -ValueType DWORD ) -eq 1) {
					$Information += "- User Re-Processing: ApplytoAllTeachersOnNextRun re-requested on next run as STEPER is running in Simulation mode"	
					$ApplytoAllTeachersOnNextRun = $true
					$ApplytoAllonNextRunSummary += [PSCustomObject]@{
						'Function' = "ApplytoAllTeachersOnNextRun re-requested on next run as STEPER is running in Simulation mode"
						'Teachers' = 0
						'Students' = ''
					}
				}
			}
		} else {
			if ((Get-RegistryValue -Path "$regpath" -Name 'ApplytoAllTeachersOnNextRun' -ValueType DWORD ) -eq 1) {
				$Information += "- User Re-Processing: ApplytoAllTeachersOnNextRun re-requested on next run as STEPER has encountered Errors"
				$ApplytoAllTeachersOnNextRun = $true
				$ApplytoAllonNextRunSummary += [PSCustomObject]@{
					'Function' = "ApplytoAllTeachersOnNextRun re-requested on next run as STEPER has encountered Errors"
					'Teachers' = 0
					'Students' = ''
				}				
			}
		}
	}
	if ($ApplytoAllStudentsOnNextRun -eq $true) {
		Set-RegistryValue -Path $regpath -Name 'ApplytoAllStudentsOnNextRun' -ValueType DWORD -Value 1
		if (($ApplytoAllonNextRunSummary.Students | measure-object -sum).sum -gt 0) {
			$Information += "- User Re-Processing: ApplytoAllStudentsOnNextRun requested Due to Failed Users, or Errors or Warnings"
		} else {
			$Information += "- User Re-Processing: ApplytoAllStudentsOnNextRun requested Due to Errors or Warnings"
		}
	} else {
		if ($MajorErrors.Count -eq 0) {
			if ($MakeChanges -eq $true) {
				Remove-ItemProperty -Path $regpath -Name 'ApplytoAllStudentsOnNextRun' -Force -Erroraction SilentlyContinue		
				$Information += "- User Re-Processing: ApplytoAllStudentsOnNextRun not re-requested on next run"
			} else {
				if ((Get-RegistryValue -Path "$regpath" -Name 'ApplytoAllStudentsOnNextRun' -ValueType DWORD ) -eq 1) {
					$Information += "- User Re-Processing: ApplytoAllStudentsOnNextRun re-requested on next run as STEPER is running in Simulation mode"	
					$ApplytoAllStudentsOnNextRun = $true 
					$ApplytoAllonNextRunSummary += [PSCustomObject]@{
						'Function' = "ApplytoAllStudentsOnNextRun re-requested on next run as STEPER is running in Simulation mode"
						'Teachers' = ''
						'Students' = 0
					}
				}
			}
		} else {
			if ((Get-RegistryValue -Path "$regpath" -Name 'ApplytoAllStudentsOnNextRun' -ValueType DWORD ) -eq 1) {
				$Information += "- User Re-Processing: ApplytoAllStudentsOnNextRun re-requested on next run as STEPER has encountered Errors"
				$ApplytoAllStudentsOnNextRun = $true
				$ApplytoAllonNextRunSummary += [PSCustomObject]@{
					'Function' = "ApplytoAllStudentsOnNextRun re-requested on next run as STEPER has encountered Errors"
					'Teachers' = ''
					'Students' = 0
				}
			}
		}
	}
}


if ($ApplytoAllTeachersOnNextRun -eq $true -or $ApplytoAllStudentsOnNextRun -eq $true -or $GlobalOnFailureApplytoAllUsersOnNextRun -gt 0) {
	if ($DetailedLoggingEnabled) {
		Add-Content -Path "$LogFile" -Value "`r`nUser Problem Resolution - User Re-Processing" -Encoding Unicode
		Add-Content -Path "$LogFile" -Value "============================================`r`n" -Encoding Unicode
		Add-Content -Path "$LogFile" -Value "User Re-Processing / Apply to All on Next Run Causes:" -Encoding Unicode
		Add-Content -Path "$LogFile" -Value "-----------------------------------------------------" -Encoding Unicode
		if ($ApplytoAllonNextRunSummary.count -gt 0) {
			$ApplytoAllonNextRunSummary | ft * -AutoSize | Out-String -Width 4096 | Out-File -filepath "$LogFile" -append -Encoding Unicode
			$TotalUsersInstancestobeReprocessed=($ApplytoAllonNextRunSummary.Teachers | measure-object -sum).sum + ($ApplytoAllonNextRunSummary.Students | measure-object -sum).sum
			Add-Content -Path "$LogFile" -Value "There are $($ApplytoAllonNextRunSummary.count) User Re-Processing / Apply to All on Next Run Causes from $TotalUsersInstancestobeReprocessed user instances`r`n" -Encoding Unicode
			Add-Content -Path "$LogFile" -Value "Apply to All Teachers on Next Run: $ApplytoAllTeachersOnNextRun" -Encoding Unicode
			Add-Content -Path "$LogFile" -Value "Apply to All Students on Next Run: $ApplytoAllStudentsOnNextRun`r`n" -Encoding Unicode
		} else {
			Add-Content -Path "$LogFile" -Value "`r`nThere are no User Re-Processing / Apply to All on Next Run Causes`r`n" -Encoding Unicode
		}
	}
	if ($regpath) {
		if ($ApplytoAllonNextRunSummary.count -gt 0) {
			write-host "User Re-Processing - There are $($ApplytoAllonNextRunSummary.count) Apply to All Teachers or Students on Next Run Causes" -foregroundcolor yellow
		} else {
			write-host "User Re-Processing - There are no User Re-Processing / Apply to All Teachers or Students on Next Run Causes" -foregroundcolor white	
		}
	}
}



if ($DetailedLoggingEnabled) {
    Add-Content -Path "$LogFile" -Value "`r`nFinishing Up" -Encoding Unicode
    Add-Content -Path "$LogFile" -Value "============`r`n" -Encoding Unicode
}

write-host
write-host "$screenstring" -foregroundcolor green 
write-host

if ($ConnectedtoMicrosoftExchange -eq $true -or $ConnectedtoAzureAD -eq $true -or $ConnectedtoTeams -eq $true) {
    write-host "Closing Connections" -foregroundcolor green
    if ($DetailedLoggingEnabled) {
        Add-Content -Path "$LogFile" -Value "`r`nClosing Connections`r`n" -Encoding Unicode      
    }
} else {
    if ($DetailedLoggingEnabled) {
        Add-Content -Path "$LogFile" -Value "`r`n(No Connections to close)`r`n" -Encoding Unicode      
    }
}

# Close Connections
#if ($ConnectedtoMSOLService -eq $true) {
    # there is no close function for this
#}


if ($ConnectedtoMicrosoftExchange -eq $true) {
#        Disconnect-ExchangeOnline -Confirm:$false -Verbose:$false -InformationAction SilentlyContinue | out-null  # its not silent
    Disconnect-ExchangeOnline -Confirm:$false -Verbose:$false -WarningAction SilentlyContinue | out-null  # its not silent
}


if ($ConnectedtoAzureAD -eq $true) {
    # remove connection policy 
    #if (($iniFile.Misc.RemediateSkypeAccessTokenTimeout -eq $true -or $iniFile.Misc.RemediateSkypeAccessTokenTimeout -eq 1) -and $tokenPolicy) {
    if ($iniFile.Misc.CreateAzureADAccessPolicy -eq $true -or $iniFile.Misc.CreateAzureADAccessPolicy -eq 1) {
        
# v1.0.1 dont remove STEPER policy if STEPER is running elsewhere

# check to see if policy should be removed irrespective if $NeedsMicrosoftTeams -eq $false

        $foundrunning = $false
        $steperprofiles = (get-childitem "$regalt\Profiles" -Erroraction SilentlyContinue).Name
		
		[double]$AssumeAzureADAccessPolicyNotRequiredTimeoutHours=$iniFile.Misc.AssumeAzureADAccessPolicyNotRequiredTimeoutHours -as [double]
        if ($AssumeAzureADAccessPolicyNotRequiredTimeoutHours -lt 0 -or $AssumeAzureADAccessPolicyNotRequiredTimeoutHours -gt 168) { [double]$AssumeAzureADAccessPolicyNotRequiredTimeoutHours = 24 }

        if ($DetailedLoggingEnabled) {
            Add-Content -Path "$LogFile" -Value "Assume Azure AD Access Policy Not Required Timeout setting in hours: $AssumeAzureADAccessPolicyNotRequiredTimeoutHours" -Encoding Unicode
			Add-Content -Path "$LogFile" -Value "Number of STEPER Profiles in the Registry: $($steperprofiles.count)" -Encoding Unicode
			if ($steperprofiles.count -gt 0) {
				$steperprofiles | fl * | Out-String -Width 4096 | Out-File -filepath "$LogFile" -append -Encoding Unicode	
			}
        }
		$Information += "- Assume Azure AD Access Policy Not Required Timeout setting in hours: $AssumeAzureADAccessPolicyNotRequiredTimeoutHours"

        foreach ($steperprofile in $steperprofiles) {
        
            $reglocation = $steperprofile.SpliT('\') | select -Last 1
            $reglocation = "$regalt\Profiles\$reglocation"
            if ($reglocation -ne $regpath) {
                $founddomain = Get-RegistryValue -Path "$reglocation" -Name 'OnMicrosoftCOMPrefix' -ValueType String 
                if (($iniFile.Misc.DontCheckOnMicrosoftCOMPrefixWhenRemovingAzureADAccessPolicy -eq $true -or $iniFile.Misc.DontCheckOnMicrosoftCOMPrefixWhenRemovingAzureADAccessPolicy -eq 1) -or ( $founddomain -eq $iniFile.Tenancy.OnMicrosoftCOMPrefix)) {

                if ((Get-RegistryValue -Path "$reglocation" -Name 'CurrentlyRunning' -ValueType DWORD ) -eq 1 -and (Get-RegistryValue -Path $reglocation -Name 'UsingAzureADAccessPolicy' -ValueType DWORD ) -gt 0) {    # an instance - not this instance - is running, and is also using remediation
                    $foundrunning=$true
                    $runtime = (Get-RegistryValue -Path $reglocation -Name 'StartTime' -ValueType String )
                        if ($runtime) {
                            $timenow = (Get-Date -format "dd/MM/yyyy HH:mm:ss")
                            $runhours =(New-TimeSpan -Start $runtime -End $timenow )
                            if ($runhours.TotalHours -ge $AssumeAzureADAccessPolicyNotRequiredTimeoutHours) { # gt 24 - 1.0.52
                                $foundrunning = $false
                            }
                        } else {
                            $foundrunning = $false
                        }
                    }
                }
            }
        }

        if ($foundrunning -eq $false) {

        if ($tokenPolicy -ne $null) {
                $e = $error.count
                try {
                    AzureADPreview\Remove-AzureADPOlicy -id $tokenPolicy.id
                    if ($DetailedLoggingEnabled) {
                        Add-Content -Path "$LogFile" -Value "`r`nRemoved owned Temporary Azure AD Policy`r`n" -Encoding Unicode
                    }
                    $Information += "- Removed owned Temporary Azure AD Policy"
					write-host "Removed owned Temporary Azure AD Policy" -foregroundcolor yellow
                }
                catch {
                    $errorstring = ""
                    if ($error.count -gt $e) {
			            if ($Error[0].Exception.Message) {
				            $errorstring = $Error[0].Exception.Message
			            } else {
				            $errorstring = "(unknown)"
			            }
                    }
                    if ($iniFile.Misc.ThrowWarningInsteadofErrorOnAzureADAccessPolicyFailure -eq $true -or $iniFile.Misc.ThrowWarningInsteadofErrorOnAzureADAccessPolicyFailure -eq 1) {
                        $Warnings += "- Could not remove owned Temporary Azure AD Policy - error message: $errorstring"
                        write-host "WARNING: Could not remove owned Temporary Azure AD Policy - error message: $errorstring" -foreground red
                        if ($DetailedLoggingEnabled) {
                            Add-Content -Path "$LogFile" -Value "Could not remove owned Temporary Azure AD Policy - error message: $errorstring" -Encoding Unicode
                        }
                    } else {
                        $MajorErrors += "- ERROR: Could not remove owned Temporary Azure AD Policy - error message: $errorstring"
                        write-host "ERROR: Could not remove owned Temporary Azure AD Policy - error message: $errorstring" -foreground red
                        if ($DetailedLoggingEnabled) {
                            Add-Content -Path "$LogFile" -Value "ERROR: Could not remove owned Temporary Azure AD Policy - error message: $errorstring" -Encoding Unicode
                        }
                    }
                }
            } else {
                $e = $error.count
                try {
                    $aadTokenLifetimePolicies = @(AzureADPreview\Get-AzureADPolicy | Where-Object { $_.DisplayName -eq "STEPER-AccessTokenLifetime"})
                    if ($aadTokenLifetimePolicies.count -gt 0) {                    
                        foreach ($ADPolicy in $aadTokenLifetimePolicies) {
                        #write-host "done"
                            AzureADPreview\Remove-AzureADPolicy -id $ADPolicy.id # there should only ever be one policy with the same name - if its shown here, its from a previous killed run
                        }
                        if ($DetailedLoggingEnabled) {
                            Add-Content -Path "$LogFile" -Value "`r`nRemoved existing Temporary Azure AD Policy`r`n" -Encoding Unicode
                        }
                        $Information += "- Removed existing Temporary Azure AD Policy"
						write-host "Removed existing Temporary Azure AD Policy" -foregroundcolor yellow
                    } else {
                        $Information += "- No existing Temporary Azure AD Policy to remove"
                        if ($DetailedLoggingEnabled) {
                            Add-Content -Path "$LogFile" -Value "`r`nNo existing Temporary Azure AD Policy to remove`r`n" -Encoding Unicode
                        }
                        write-host "No existing Temporary Azure AD Policy to remove" -foregroundcolor yellow
                    }
                }
                catch {
                    $errorstring = ""
                    if ($error.count -gt $e) {
			            if ($Error[0].Exception.Message) {
				            $errorstring = $Error[0].Exception.Message
			            } else {
				            $errorstring = "(unknown)"
			            }
                    }
                    if ($iniFile.Misc.ThrowWarningInsteadofErrorOnAzureADAccessPolicyFailure -eq $true -or $iniFile.Misc.ThrowWarningInsteadofErrorOnAzureADAccessPolicyFailure -eq 1) {
                        $Warnings += "- Could not remove existing Temporary Azure AD Policy - error message: $errorstring"
                        write-host "WARNING: Could not remove existing Temporary Azure AD Policy - error message: $errorstring" -foreground red
                        if ($DetailedLoggingEnabled) {
                            Add-Content -Path "$LogFile" -Value "Could not remove existing Temporary Azure AD Policy - error message: $errorstring" -Encoding Unicode
                        }
                    } else {
                        $MajorErrors += "- ERROR: Could not remove existing Temporary Azure AD Policy - error message: $errorstring"
                        write-host "ERROR: Could not remove existing Temporary Azure AD Policy - error message: $errorstring" -foreground red
                        if ($DetailedLoggingEnabled) {
                            Add-Content -Path "$LogFile" -Value "ERROR: Could not remove existing Temporary Azure AD Policy - error message: $errorstring" -Encoding Unicode
                        }
                    }
                }
            }
        } else {
            write-host "Did not remove Temporary Azure AD Policy due to another instance of STEPER that is currently running" -foreground yellow
            $Information += "- Did not remove Temporary Azure AD Policy due to another instance of STEPER that is currently running"
            if ($DetailedLoggingEnabled) {
                Add-Content -Path "$LogFile" -Value "Did not remove Temporary Azure AD Policy due to another instance of STEPER that is currently running" -Encoding Unicode
            }

        }
    Set-RegistryValue -Path $regpath -Name 'UsingAzureADAccessPolicy' -ValueType DWORD -Value 0
    }   
    AzureADPreview\Disconnect-AzureAD
}

if ($ConnectedtoTeams -eq $true) {
    Disconnect-MicrosoftTeams
}


#clean log folder

if ($MajorErrors.Count -eq 0 -and ($iniFile.Misc.CleanUpLogFilesOlderThanDays)) {
#if ($iniFile.Misc.CleanUpLogFilesOlderThanDays) {
    
    [int]$DeleteFilesDays = $iniFile.Misc.CleanUpLogFilesOlderThanDays -as [int]
    
    if ($DeleteFilesDays -gt 4) {

        write-host "Tidying Up Log Folder $DetailedLogFileFolder" -foregroundcolor green
        if ($DetailedLoggingEnabled) {
            Add-Content -Path "$LogFile" -Value "`r`nTidying Up Log Folder $DetailedLogFileFolder`r`n" -Encoding Unicode      
        }

        $Actions += "- Tidying Up Log Folder $DetailedLogFileFolder"

        $FilestoDelete = @()

        if ($DetailedLogFileFolder[$DetailedLogFileFolder.Length-1] -eq '\') {

            $FilestoDelete+= $DetailedLogFileFolder + "STEPER v* Log File.txt"
            if ($iniFile.InformationRetrieval.GetAllUserPolicyInformation -eq $true -or $iniFile.InformationRetrieval.GetAllUserPolicyInformation -eq 1) {
                $FilestoDelete+= $DetailedLogFileFolder + "STEPER v* User Export.csv"
            }
            $FilestoDelete+= $DetailedLogFileFolder + "STEPER v* Log Bundle.zip"
        } else {
            $FilestoDelete+= $DetailedLogFileFolder + "\STEPER v* Log File.txt"
            if ($iniFile.InformationRetrieval.GetAllUserPolicyInformation -eq $true -or $iniFile.InformationRetrieval.GetAllUserPolicyInformation -eq 1) {
                $FilestoDelete+= $DetailedLogFileFolder + "\STEPER v* User Export.csv"
            }
            $FilestoDelete+= $DetailedLogFileFolder + "\STEPER v* Log Bundle.zip"
        }

        $CurrentDate = Get-Date
        $DatetoDelete = $CurrentDate.AddDays(-$DeleteFilesDays)

        foreach ($FileSpec in $FilestoDelete) {
            write-host "Cleaning up log files that are $DeleteFilesDays days old and older - $FileSpec" -ForegroundColor green
            if ($DetailedLoggingEnabled) {
                Add-Content -Path "$LogFile" -Value "Cleaning up log files that are $DeleteFilesDays days old and older - $FileSpec" -Encoding Unicode      
            }
            Get-ChildItem $FileSpec | Where-Object { $_.LastWriteTime -lt $DatetoDelete } | Remove-Item
        }
    }
}


if ($Motd -ne "(no message)") {
	if ($MotdType -eq 2) {
		$Warnings += "$Motd"
	}
	if ($MotdType -eq 3) {
		$MajorErrors += "$Motd"
	}
}


# tidy up strings
if ($MajorErrors.Count -gt 0) {
    $MajorErrorsCR = $MajorErrors | Out-String
} else {
    $MajorErrorsCR = "(no errors)" | out-string
}

if ($Warnings.Count -gt 0) {
    $WarningsCR = $Warnings | Out-String
} else {
    $WarningsCR = "(no warnings)" | out-string
}

$UsefulActions=0

if ($Results.Count -gt 0) {
    $ResultsCR = ( $Results | ft * -AutoSize | Out-String -Width 4096 )
    $TotalChanges =($Results.Quantity | measure-object -sum).sum
    $UsefulActions = @($Results | where { $_.Quantity -gt 0}).count
    if ($MakeChanges -eq $true) {
        $Actions += "Action Summary: $TotalChanges changes made through $UsefulActions Actions"
    } else {
        $Actions += "Action Summary: Simulation: $TotalChanges changes would be made through $UsefulActions Actions"
    }
} else {
    $ResultsCR = "(no results)" | Out-String
}

if ($Information.Count -gt 0) {
    $InformationCR = $Information | Out-String -Width 4096 
} else {
    $InformationCR = "(no information)" | out-string
}
if ($Actions.Count -gt 0) {
    $ActionsCR = $Actions | Out-String
} else {
    $ActionsCR = "(no actions)" | Out-String
}
if ($ApplytoAllonNextRunSummary.count -gt 0) {
	$ApplytoAllonNextRunSummaryCR = $ApplytoAllonNextRunSummary | Out-String
} else {
	$ApplytoAllonNextRunSummaryCR = "(no actions)" | Out-String
}

# finish up
$endtime = (Get-Date -format "dd/MM/yyyy HH:mm:ss")
[int]$processingtime =(New-TimeSpan -Start $starttime -End $endtime).TotalSeconds

$timestring=""

if ($processingtime -ge 86400) {
    $days = [math]::floor($processingtime/86400)
    $processingtime = $processingtime - ($days * 86400)

    if ($days -ne 1) {
        $timestring = " $days days"
    } else {
        $timestring = " $days day"
    }
}

if ($processingtime -ge 3600) {
    $hours = [math]::floor($processingtime/3600)
    $processingtime = $processingtime - ($hours * 3600)

    if ($hours -ne 1) {
        $timestring = "$timestring $hours hrs"
    } else {
        $timestring = "$timestring $hours hr"
    }
}

$minutes = [math]::floor($processingtime/60)
$processingtime = $processingtime - ($minutes * 60)
if ($minutes -ne 1) {
    $timestring = "$timestring $minutes mins"
} else {
    $timestring = "$timestring $minutes min"
}

if ($processingtime -ne 1) {
    $timestring = "$timestring $processingtime secs"
} else {
    $timestring = "$timestring $processingtime sec"
}

$timestring = $timestring.Trim()

if ($DontRecordCompletionTime -eq $false -and $regpath) {
    Set-RegistryValue -Path $regpath -Name 'CompletionTime' -ValueType String -Value $endtime
	Set-RegistryValue -Path $regpath -Name 'ProcessingTime' -ValueType String -Value $timestring
    Set-RegistryValue -Path $regpath -Name 'CurrentlyRunning' -ValueType DWORD -Value 0

    Set-RegistryValue -Path "$regpath\Program Information\$Hash" -Name 'CompletionTime' -ValueType String -Value $endtime
    Set-RegistryValue -Path "$regpath\Program Information\$Hash" -Name 'UserName' -ValueType String -Value $username
    Set-RegistryValue -Path "$regpath\Program Information\$Hash" -Name 'Version' -ValueType String -Value $Version
	Set-RegistryValue -Path "$regpath\Program Information\$Hash" -Name 'Channel' -ValueType String -Value $Channel
    Set-RegistryValue -Path "$regpath\Program Information\$Hash" -Name 'SettingsFile' -ValueType String -Value $SettingsFileName
	Set-RegistryValue -Path "$regpath\Program Information\$Hash" -Name 'Location' -ValueType String -Value $Myname
	Set-RegistryValue -Path "$regpath\Program Information\$Hash" -Name 'Username' -ValueType String -Value $Username

    Set-RegistryValue -Path $regpath -Name 'MajorErrors' -ValueType MultiString -Value $MajorErrorsCR
    Set-RegistryValue -Path $regpath -Name 'MajorErrorCount' -ValueType DWORD -Value $MajorErrors.Count

    Set-RegistryValue -Path $regpath -Name 'Warnings' -ValueType MultiString -Value $WarningsCR
    Set-RegistryValue -Path $regpath -Name 'WarningCount' -ValueType DWORD -Value $Warnings.Count

    Set-RegistryValue -Path $regpath -Name 'Actions' -ValueType MultiString -Value $ActionsCR
    Set-RegistryValue -Path $regpath -Name 'ActionCount' -ValueType DWORD -Value $Actions.Count

    Set-RegistryValue -Path $regpath -Name 'Information' -ValueType MultiString -Value $InformationCR
    Set-RegistryValue -Path $regpath -Name 'InformationCount' -ValueType DWORD -Value $Information.Count

    Set-RegistryValue -Path $regpath -Name 'Results' -ValueType MultiString -Value $ResultsCR
    Set-RegistryValue -Path $regpath -Name 'ResultsCount' -ValueType DWORD -Value $Results.Count

    Set-RegistryValue -Path $regpath -Name 'ResultsNonZeroCount' -ValueType DWORD -Value $UsefulActions
    Set-RegistryValue -Path $regpath -Name 'ProcessingTimeinSeconds' -ValueType DWORD -Value $processingtime
	
	Set-RegistryValue -Path $regpath -Name 'ApplytoAllonNextRunSummary' -ValueType MultiString -Value $ApplytoAllonNextRunSummaryCR
    Set-RegistryValue -Path $regpath -Name 'ApplytoAllonNextRunSummaryCount' -ValueType DWORD -Value $ApplytoAllonNextRunSummary.Count
	
	Set-RegistryValue -Path $regpath -Name 'OtherSettings' -ValueType MultiString -Value $OtherSettingsCR
    Set-RegistryValue -Path $regpath -Name 'OtherSettingsCount' -ValueType DWORD -Value $OtherSettings.Count
}


if ($DetailedLoggingEnabled) {
    Add-Content -Path "$LogFile" -Value "`r`n**************************************************************************************************************************************************************************************************`r`n" -Encoding Unicode

	Add-Content -Path "$LogFile" -Value "`r`nMOTD:`r`n=====" -Encoding Unicode

if ($MotdType -eq 1 -or $Motd -eq "(no message)") {
	Add-Content -Path "$LogFile" -Value "$Motd" -Encoding Unicode
} else {
	if ($MotdType -eq 2) {
        write-host
        write-host "$screenstring" -foregroundcolor green
	    write-host

		write-host "WARNING: $Motd" -foregroundcolor yellow
		#$Warnings += "$Motd"
		write-host
	}

	if ($MotdType -eq 3) {
        write-host
        write-host "$screenstring" -foregroundcolor green
	    write-host

		write-host "ERROR: $Motd" -foregroundcolor red
		#$MajorErrors += "$Motd"
		write-host
	}
}
    Add-Content -Path "$LogFile" -Value "`r`nActions:`r`n========`r`n$ActionsCR" -Encoding Unicode
    Add-Content -Path "$LogFile" -Value "Results:`r`n========`r`n$ResultsCR" -Encoding Unicode
    Add-Content -Path "$LogFile" -Value "Information:`r`n============`r`n$InformationCR" -Encoding Unicode
    
    if ($MajorErrors.Count -gt 0 ) {
        Add-Content -Path "$LogFile" -Value "`r`nErrors and Warnings:`r`n====================`r`n$($MajorErrors.Count) Errors Encountered and $($Warnings.Count) Warnings" -Encoding Unicode
    } else {
        if ($Warnings.Count -gt 0 ) {
            Add-Content -Path "$LogFile" -Value "`r`nErrors and Warnings:`r`n====================`r`n$($Warnings.Count) Warnings Encountered" -Encoding Unicode
        } else {
            Add-Content -Path "$LogFile" -Value "`r`nErrors and Warnings:`r`n====================`r`nNo Issues Encountered" -Encoding Unicode
        }
    }
    
    Add-Content -Path "$LogFile" -Value "`r`nErrors:`r`n-------`r`n$MajorErrorsCR" -Encoding Unicode
    Add-Content -Path "$LogFile" -Value "Warnings:`r`n---------`r`n$WarningsCR" -Encoding Unicode
    Add-Content -Path "$LogFile" -Value "Settings Summary:`r`n=================" -Encoding Unicode
    Add-Content -Path "$LogFile" -Value "`r`nCredentials:`r`n------------`r`n$CredentialsCR" -Encoding Unicode
    Add-Content -Path "$LogFile" -Value "M365:`r`n-----`r`n$TenancyCR" -Encoding Unicode
    Add-Content -Path "$LogFile" -Value "Source Groups:`r`n--------------`r`n$SourceGroupsCR" -Encoding Unicode
    Add-Content -Path "$LogFile" -Value "Licensing Information in Use:`r`n-----------------------------`r`n$LicensingInformationCR" -Encoding Unicode
    Add-Content -Path "$LogFile" -Value "All Teachers / All Students Unified Groups:`r`n-------------------------------------------`r`n$UnifiedGroupsCR" -Encoding Unicode
    Add-Content -Path "$LogFile" -Value "Department:`r`n-----------`r`n$DepartmentCR" -Encoding Unicode
	if ($MajorErrors.Count -gt 0 ){
		Add-Content -Path "$LogFile" -Value "Filtered Security Groups:`r`n-------------------------`r`n$FilteredSecurityGroupsToProcessCR" -Encoding Unicode
		Add-Content -Path "$LogFile" -Value "Filtered Unified Groups:`r`n------------------------`r`n$FilteredUnifiedGroupsToProcessCR" -Encoding Unicode
		Add-Content -Path "$LogFile" -Value "Filtered Distribution Groups:`r`n-----------------------------`r`n$FilteredDistributionGroupsToProcessCR" -Encoding Unicode
		Add-Content -Path "$LogFile" -Value "Packages:`r`n---------`r`n$PackagesToProcessCR" -Encoding Unicode
		Add-Content -Path "$LogFile" -Value "Policies:`r`n---------`r`n$PoliciesToProcessCR" -Encoding Unicode
	}
    Add-Content -Path "$LogFile" -Value "Results Emails:`r`n---------------`r`n$EmailInfoCR" -Encoding Unicode
    Add-Content -Path "$LogFile" -Value "Other Settings:`r`n---------------`r`n$OtherSettingsCR" -Encoding Unicode

    Add-Content -Path "$LogFile" -Value "Package Providers" -Encoding Unicode
    Add-Content -Path "$LogFile" -Value "=================" -Encoding Unicode
    Get-PackageProvider | Select Name, Version | ft * -AutoSize | Out-String -Width 4096 | Out-File -filepath "$LogFile" -append -Encoding Unicode
    
    Add-Content -Path "$LogFile" -Value "Installed Modules" -Encoding Unicode
    Add-Content -Path "$LogFile" -Value "=================" -Encoding Unicode
    
    Get-installedmodule | Select Name, Version, Type, InstalledDate, PublishedDate,ProjectURI | ft *  -AutoSize | Out-String -Width 4096 | Out-File -filepath "$LogFile" -append -Encoding Unicode

    Add-Content -Path "$LogFile" -Value "Modules" -Encoding Unicode
    Add-Content -Path "$LogFile" -Value "=======" -Encoding Unicode

    Get-module | Select Name, Version, ModuleType | ft * -AutoSize | Out-String -Width 4096 | Out-File -filepath "$LogFile" -append -Encoding Unicode

    Add-Content -Path "$LogFile" -Value "Powershell Version" -Encoding Unicode
    Add-Content -Path "$LogFile" -Value "==================" -Encoding Unicode

    $PSversiontable | ft * -AutoSize | Out-String -Width 4096 | Out-File -filepath "$LogFile" -append -Encoding Unicode

    Add-Content -Path "$LogFile" -Value "System Information" -Encoding Unicode
    Add-Content -Path "$LogFile" -Value "==================" -Encoding Unicode    
    
    Add-Content -Path "$LogFile" -Value "`r`nComputer Name: $hostname" -Encoding Unicode
	$string  = (get-wmiobject -class win32_operatingsystem).Version
	Add-Content -Path "$LogFile" -Value "OS Version: $string" -Encoding Unicode
	$string  = (get-wmiobject -class win32_operatingsystem).BuildNumber
	Add-Content -Path "$LogFile" -Value "OS Build Number: $string" -Encoding Unicode
    Add-Content -Path "$LogFile" -Value "Username: $Username" -Encoding Unicode
    Add-Content -Path "$LogFile" -Value "Settings File Version: $($iniFile.Misc.SettingsFileVersion)" -Encoding Unicode
	Add-Content -Path "$LogFile" -Value "Channel / PatchLevel: $Channel / $PatchLevel" -Encoding Unicode
    Add-Content -Path "$LogFile" -Value "Hash: $Hash" -Encoding Unicode
    Add-Content -Path "$LogFile" -Value "`r`nRun Completed: $endtime" -Encoding Unicode
    Add-Content -Path "$LogFile" -Value "Processing Time: $timestring" -Encoding Unicode
	Add-Content -Path "$LogFile" -Value "Original TLS Settings: $tlsversion" -Encoding Unicode
	$tlsversion = ([Net.ServicePointManager]::SecurityProtocol)
	Add-Content -Path "$LogFile" -Value "Running or Updated TLS Settings: $tlsversion" -Encoding Unicode
}


# emails and event logging
if ($MajorErrors.Count -gt 0 ) {
	write-host
	write-host "$screenstring" -foregroundcolor green 
	write-host
	write-host "Errors:" -ForegroundColor red
	#write-host "-------" -ForegroundColor red
	foreach ($string in $MajorErrors) {
		write-host "$string" -ForegroundColor red
	}
	write-host	
    $String = "Errors Encountered"
    if ($DetailedLoggingEnabled) {
        write-host "Processing ended with $($MajorErrors.Count) Errors and $($Warnings.Count) Warnings - review the Application Event Log and Logfile $LogFile" -ForegroundColor red
    } else {
        write-host "Processing ended with $($MajorErrors.Count) Errors and $($Warnings.Count) Warnings - review the Application Event Log" -ForegroundColor Red
    }
} else {
    if ($Warnings.Count -gt 0 ) {
		write-host
		write-host "$screenstring" -foregroundcolor green 
		write-host
		write-host "Warnings:" -ForegroundColor yellow
		#write-host "---------" -ForegroundColor yellow
		foreach ($string in $Warnings) {
			write-host "$string" -ForegroundColor yellow
		}
		write-host			
		
        $String = "Warnings Encountered"
        if ($DetailedLoggingEnabled) {
            write-host "Processing ended with $($Warnings.Count) Warnings - review the Application Event Log and Logfile $LogFile" -ForegroundColor Yellow
        } else {
           write-host "Processing ended with $($Warnings.Count) Warnings - review the Application Event Log" -ForegroundColor Yellow
        }
    } else {
        $String = "Processing ended"

        if ($DetailedLoggingEnabled) {
            write-host "Processing ended with no problems detected - review the Application Event Log and Logfile $LogFile" -ForegroundColor green
        } else {
           write-host "Processing ended with no problems detected - review the Application Event Log" -ForegroundColor green
        }
    }
}

if ($DetailedLoggingEnabled) {
    Compress-Archive -Path $LogFile -DestinationPath $ZipFile -Update
    if ($CopySettings -and $iniFile.Misc.DisableSaveSettingsWithLogZIP -ne $true -and $iniFile.Misc.DisableSaveSettingsWithLogZIP -ne 1) {
        #Get-Content $SettingsFileName | Select-String -pattern "GA_Password=" -notmatch | Out-File $CopySettings
        #Get-Content $SettingsFileName |  ForEach-Object { $_ -replace "GA_Password=.+" , "GA_Password=[hidden]" } | Set-Content $CopySettings
        if (Test-Path $CopySettings) { Remove-Item $CopySettings }
        if ($iniFile.Credentials.GA_Password) {
			$inifilecontents | ForEach-Object { $_ -replace "GA_Password=.+" , "GA_Password=[hidden]" } | ForEach-Object { $_ -replace "$($iniFile.Credentials.GA_Password)", "[hidden]" } | ForEach-Object { $_ -replace "ProtectedGA_Password=.+" , "ProtectedGA_Password=[hidden]" } | Set-Content $CopySettings
		} else {
			$inifilecontents | ForEach-Object { $_ -replace "GA_Password=.+" , "GA_Password=[hidden]" } | ForEach-Object { $_ -replace "ProtectedGA_Password=.+" , "ProtectedGA_Password=[hidden]" } | Set-Content $CopySettings			
		}
        Compress-Archive -Path $CopySettings -DestinationPath $ZipFile -Update
        Remove-Item $CopySettings
    }
}
if ($UserFile) {
    Compress-Archive -Path $UserFile -DestinationPath $ZipFile -Update
}


$Priority = "Low"
if ($MajorErrors.Count -gt 0 -or $Warnings.Count -gt 0) {
    $Priority = "High"
}

$EntryType = 'Information'
if ($Warnings.Count -gt 0) { $EntryType = 'Warning' }
if ($MajorErrors.Count -gt 0) { $EntryType = 'Error' }

write-host
write-host "$screenstring" -foregroundcolor green 
write-host
write-host "Processing Time: $timestring" -ForegroundColor white

Write-EventLog –LogName Application –Source $AppLogName –EntryType $EntryType –EventID ($AppLogBaseIndex+2) -Category $AppLogCategory -Message ("$Caption - $ProgramName Version: $Version - $String`r`nChannel / PatchLevel: $Channel / $PatchLevel`r`nPID: $Pid`r`n`r`nSettings File: $SettingsFileName`r`nProfile Name: $ProfileName`r`n`r`nBound / Recognised Command Line Arguments:`r`n$PSBoundParametersCR`r`nUnbound / Unrecognised Command Line Arguments:`r`n$UnBoundParametersCR`r`nActions:`r`n$ActionsCR`r`nResults:`r`n$ResultsCR`r`nErrors:`r`n$MajorErrorsCR`r`nWarnings:`r`n$WarningsCR`r`nInformation:`r`n$InformationCR`r`nSource Groups:`r`n$SourceGroupsCR`r`nLicensing Information in Use:`r`n$LicensingInformationCR`r`nStart Time: $starttime`r`nEnd Time: $endtime`r`nProcessing Time: $timestring`r`nUsername: $Username`r`nHash: $Hash")
if ($EmailAvailable -eq $true) {
    write-host "Sending results email to $EmailToAddress" -ForegroundColor green
	if (($iniFile.ResultsEmail.OnlySendEmailonWarningorError -ne $true -and $iniFile.ResultsEmail.OnlySendEmailonWarningorError -ne 1) -or $MajorErrors.Count -gt 0 -or $Warnings.Count -gt 0 ) {
		
		if ($iniFile.Tenancy.OnMicrosoftCOMPrefix) {
			$Domain = $iniFile.Tenancy.OnMicrosoftCOMPrefix
		} else {
			if ($iniFile.Credentials.GA_Username) {
				$Domain = $iniFile.Credentials.GA_Username.Split('@')[1]
			} else {
				$Domain = "(unknown)"
			}
		}
	
		if ( Test-Path $Zipfile) {
            $e = $error.count
            Send-MailMessage -From $EmailFromAddress -To $EmailToAddress -Subject "$ProgramName - Profile Name: $ProfileName - Domain: $Domain - $String" -Body "<font face='courier'><pre>$Caption - $ProgramName Version: $Version - $String`r`n`r`nSettings File: $SettingsFileName`r`n`r`nProfile Name: $ProfileName`r`n`r`nActions:`r`n$ActionsCR`r`nResults:`r`n$ResultsCR`r`nErrors:`r`n$MajorErrorsCR`r`nWarnings:`r`n$WarningsCR`r`nInformation:`r`n$InformationCR`r`nSource Groups:`r`n$SourceGroupsCR`r`nLicensing Information in Use:`r`n$LicensingInformationCR`r`nStart Time: $starttime`r`nEnd Time: $endtime`r`nProcessing Time: $timestring`r`nComputer Name: $hostname`r`nChannel / PatchLevel: $Channel / $PatchLevel`r`nPID: $Pid`r`nHash: $Hash</pre></font>" -BodyAsHtml -SmtpServer $EmailServer -Port $EmailPort -Credential $GACreds -Priority $Priority -UseSsl:$EmailSSL -Attachments $Zipfile
		} else {
            $e = $error.count
			Send-MailMessage -From $EmailFromAddress -To $EmailToAddress -Subject "$ProgramName - Profile Name: $ProfileName - Domain: $Domain - $String" -Body "<font face='courier'><pre>$Caption - $ProgramName Version: $Version - $String`r`n`r`nSettings File: $SettingsFileName`r`n`r`nProfile Name: $ProfileName`r`n`r`nActions:`r`n$ActionsCR`r`nResults:`r`n$ResultsCR`r`nErrors:`r`n$MajorErrorsCR`r`nWarnings:`r`n$WarningsCR`r`nInformation:`r`n$InformationCR`r`nSource Groups:`r`n$SourceGroupsCR`r`nLicensing Information in Use:`r`n$LicensingInformationCR`r`nStart Time: $starttime`r`nEnd Time: $endtime`r`nProcessing Time: $timestring`r`nComputer Name: $hostname`r`nChannel / PatchLevel: $Channel / $PatchLevel`r`nPID: $Pid`r`nHash: $Hash</pre></font>" -BodyAsHtml -SmtpServer $EmailServer -Port $EmailPort -Credential $GACreds -Priority $Priority -UseSsl:$EmailSSL 
		}
	    if ($error.count -gt $e) {
            $errorstring = ""
			if ($Error[0].Exception.Message) {
				$errorstring = $Error[0].Exception.Message
			} else {
				$errorstring = "(unknown)"
			}
            write-host
            write-host "$screenstring" -foregroundcolor green
	        write-host
            write-host "Failed to send results email to $EmailToAddress - error message: $errorstring" -ForegroundColor Red
            $Warnings += "- Failed to send results email to $EmailToAddress - error message: $errorstring"
			Write-EventLog –LogName Application –Source $AppLogName –EntryType Error –EventID ($AppLogBaseIndex+7) -Category $AppLogCategory -Message ("$Caption - $ProgramName Version: $Version - Failed to send results email`r`nChannel / PatchLevel: $Channel / $PatchLevel`r`nPID: $Pid`r`n`r`nFailed to send results email to $EmailToAddress - error message: $errorstring")
            if ($DetailedLoggingEnabled) {
                Add-Content -Path "$LogFile" -Value "`r`n**************************************************************************************************************************************************************************************************`r`n" -Encoding Unicode
				Add-Content -Path "$LogFile" -Value "Failed to send results email to $EmailToAddress - error message: $errorstring" -Encoding Unicode
                Compress-Archive -Path $LogFile -DestinationPath $ZipFile -Update
            }
        }
    }
}

if ($MyInvocation.InvocationName -eq '&') { # Called via Explorer Window
    [string]$string=""
	if ($MajorErrors.Count -gt 0) {
		if ($Warnings.Count -gt 0) {
			$string=" with Errors and Warnings"
		} else {
			$string=" with Errors"
		}
    } else {
        if ($Warnings.Count -gt 0) {
        $string=" with Warnings"    
        }
    }
	$Host.UI.RawUI.WindowTitle = "** COMPLETED" + $string + " ** " + $myInvocation.MyCommand.Definition + " " + $ProgramName + " " + $Version + " (C:" + $Channel + ":" + $PatchLevel + ") Profile: " + $ProfileName + " (Elevated)"
    write-host
    write-host "Note: This window will close in 30 seconds, or you may close it now" -foregroundcolor green
    start-sleep -Seconds 30
} # else Called via Existing Prompt

if ($MajorErrors.Count -eq 0 ) {
    exit 0  # no error
} else {
    exit $MajorErrors.Count  # failure
}
# the end