FortigiAutomationLibary.psm1

#######################################################################################################
# FortigiAutomationLibary
#
# By Maatschap Fortigi
# Wim van den Heijkant (Wim@Fortigi.nl)
#
#######################################################################################################
#Basic Functions

Function Send-Email  {  
    param (
       [Parameter(Mandatory=$True)]  
       [PSCredential] $AzureO365Credential,    
       [Parameter(Mandatory=$False)]  
       [string] $MessageBody,  
       [Parameter(Mandatory=$False)]  
       [string] $MessageSubject,  
       [Parameter(Mandatory=$True)]  
       [string] $MessageTo,  
       [Parameter(Mandatory=$True)]  
       [string] $MessageFrom 
        )      
  
  
 
   
        $Message = New-Object System.Net.Mail.MailMessage  
        $Message.From = $MessageFrom 
        $Message.replyTo = $MessageFrom 
        $Message.To.Add($MessageTo) 
        $Message.Body = $MessageBody 
        $Message.BodyEncoding = ([System.Text.Encoding]::UTF8)  
        $Message.IsBodyHtml = $true 
        $Message.SubjectEncoding = ([System.Text.Encoding]::UTF8) 
          
        try 
        { 
            $Message.Subject = $MessageSubject 
        } 
        catch [System.Management.Automation.SetValueInvocationException] 
        { 
            "An exception was ignored while setting the message subject.`n{0}" -f $_.Exception | Write-Verbose 
        } 
  
        $SmtpClient = New-Object System.Net.Mail.SmtpClient 'smtp.office365.com', 587 
        $SmtpClient.Credentials = $AzureO365Credential 
        $SmtpClient.EnableSsl   = $true 
  
        $SmtpClient.Send($Message) 
  } 

Function Get-Temppassword() {

    [int]$length=4
    $alphabet=$NULL;For ($a=65;$a –le 90;$a++) {$alphabet+=,[char][byte]$a }

    For ($loop=1; $loop –le $length; $loop++) {

                $TempPassword+=($alphabet| GET-RANDOM)

                }


    $TempPassword += “#”,”@”,"!","?" | GET-RANDOM
    $TempPassword += Get-Random -Minimum 100 -Maximum 999


    return $TempPassword

    }

Function Get-PasswordProfile {
    param (
        [Parameter(Mandatory=$True)]  
        [string] $Password)

    $passwordProfile = New-Object -TypeName Microsoft.Open.AzureAD.Model.PasswordProfile
    $passwordProfile.ForceChangePasswordNextLogin = “True”
    $passwordProfile.Password = $Password
    $passwordProfile
    
    }

Function Get-SingInName {
    param (
        [Parameter(Mandatory=$True)]  
        [string] $Email)

    $SignInName = New-Object -TypeName Microsoft.Open.AzureAD.Model.SignInName
    $SignInName.Type = “emailAddress”
    $SignInName.Value = $Email
    $SignInName
    }

#######################################################################################################
#Sync Functions

Function Find-SyncJoins {
    Param(
        [Parameter(Mandatory=$True)] 
        [Array]$SList,
        [Array]$DList,
        [Parameter(Mandatory=$True)] 
        [string]$SAnchor,
        [Parameter(Mandatory=$True)] 
        [string]$DAnchor
        )
    
    #This function returens Adds, Updates and Deletes
    [array]$InSourceNotDestionation = $null
    [array]$InBothSourceAndDestionation = $null
    [array]$InDestionationNotSource = $null

    #Look for $DItems in $SList
    Foreach ($SItem in $SList) {
    
        #Look for existing record
        [Array]$DItem = $DList | where-object {$_.$DAnchor -eq $SItem.$SAnchor}
        
        #$DItem does not exist in $SList
        If (!($DItem)) {
            $InSourceNotDestionation += $SItem
            }

        #$DItem does exist in $SList. Combine $SItem and $DItem in update list.
        IF ($DItem) {

            $Update = "" | Select-Object -Property SItem,DItem
            $Update.SItem = $SItem
            $Update.DItem = $DItem

            $InBothSourceAndDestionation += $Update
            }
        }

    #Find $DList Items that are not in the $SList
    Foreach ($DItem in $DList) {
    
        #Look for existing record
        $SItem = $SList | where-object {$DItem.$DAnchor -eq $_.$SAnchor}
        
        #$DItem does not exist in $SList
        If (!($SItem)) {
            $InDestionationNotSource += $DItem
            }        
        }

    $Return = "" | Select-Object -Property InSourceNotDestionation,InBothSourceAndDestionation,InDestionationNotSource
    $Return.InSourceNotDestionation = $InSourceNotDestionation
    $Return.InBothSourceAndDestionation = $InBothSourceAndDestionation
    $Return.InDestionationNotSource = $InDestionationNotSource
    Return $Return

    } 

Function Find-SyncObjectDiffSharePoint {
    Param(
        [Parameter(Mandatory=$True)] 
        [Array]$AttributeMapping,
        [Parameter(Mandatory=$True)] 
        [Array]$Update
        )
    
    $Values = $null
    Foreach ($Attribute in $AttributeMapping) {
        
        $BusinessRuleResult = "Nothing"
        If ($Attribute.BusinessRules) {

            Foreach ($BusinessRule in $Attribute.BusinessRules) {
                If ($BusinessRule.Operator -eq ">") {
                    If ($Update.SItem.($BusinessRule.SourceAttr) -lt $BusinessRule.Value) { 
                        $BusinessRuleResult = $BusinessRule.Result
                        }
                    }
                If ($BusinessRule.Operator -eq "<") {
                    If ($Update.SItem.($BusinessRule.SourceAttr) -gt $BusinessRule.Value) { 
                        $BusinessRuleResult = $BusinessRule.Result
                        }
                    }
                If ($BusinessRule.Operator -eq "=") {
                    If ($Update.SItem.($BusinessRule.SourceAttr) -eq $BusinessRule.Value) { 
                        $BusinessRuleResult = $BusinessRule.Result
                        }
                    }
                }
            }
                
        #If the business rules have given a result
        If ($BusinessRuleResult -ne "Nothing") {
            $NewValue = $BusinessRuleResult
            }
        Else {
            $NewValue = $Update.SItem.($Attribute.SourceAttr)
            }

        #Als het Source Attribute niet leeg is.
        if ($NewValue -ne $null) {
            
            #Is er een doel waarde
            iF ($Update.DItem.($Attribute.DestAttr) -ne $null) {
            
                $DestAttrType = ($Update.DItem.($Attribute.DestAttr).GetType()).Name
        
                #User lookup velden moeten we expliciet opgeven welk attribute we willen gebruiken om op te vergelijken.
                If ($DestAttrType -eq "FieldUserValue") {
                    $LookupValue = $Update.DItem.($Attribute.DestAttr).($Attribute.DestLookupAttr)
                    If ($LookupValue -ne $NewValue) {
                        $Values += @{$Attribute.DestAttr=$NewValue;} 
                        }
                    }
                
                #Andere kunnen 1 op 1 gechecked worden.
                Else {
                    IF ($Update.DItem.($Attribute.DestAttr) -ne $NewValue) {
                        $Values += @{$Attribute.DestAttr=$NewValue;} 
                        }
                    }


                }
            #Overschrijf de destination...
            Else {
                $Values += @{$Attribute.DestAttr=$NewValue;}
                }
            }

        }

    Return $Values

    }

Function New-SharePointListItem {
    Param (
        [Parameter(Mandatory=$True)] 
        [String]$SharePointListID,
        [Parameter(Mandatory=$True)] 
        [Array]$Add,
        [Parameter(Mandatory=$True)] 
        [Array]$AttributeMapping
        )
    
    $Values = @{}
    Foreach ($Attribute in $AttributeMapping) {
        
        $BusinessRuleResult = "Nothing"
        If ($Attribute.BusinessRules) {
            Foreach ($BusinessRule in $Attribute.BusinessRules) {
                If ($BusinessRule.Operator -eq ">") {
                    If ($Add.($BusinessRule.SourceAttr) -lt $BusinessRule.Value) { 
                        $BusinessRuleResult = $BusinessRule.Result
                        }
                    }
                If ($BusinessRule.Operator -eq "<") {
                    If ($Add.($BusinessRule.SourceAttr) -gt $BusinessRule.Value) { 
                        $BusinessRuleResult = $BusinessRule.Result
                        }
                    }
                If ($BusinessRule.Operator -eq "=") {
                    If ($Add.($BusinessRule.SourceAttr) -eq $BusinessRule.Value) { 
                        $BusinessRuleResult = $BusinessRule.Result
                        }
                    }
                }
            }
                
        #If the business rules have given a result
        If ($BusinessRuleResult -ne "Nothing") {
            $Values += @{$Attribute.DestAttr=$BusinessRuleResult;}
            }

        If ($Add.($Attribute.SourceAttr) -ne $null){
            $Values += @{$Attribute.DestAttr=$Add.($Attribute.SourceAttr);}
            }
        }
    
    Add-PnPListItem -List $SharePointListID -Values $Values -ContentType Item
    Write-Host "Succesfully created new SharePoint Item with Attributes: "$Values.Keys " and " $Values.Values -ForegroundColor Green
    
    }

Function Find-SyncObjectDiffAAD {
       Param(
        [Parameter(Mandatory=$True)] 
        [Array]$AttributeMapping,
        [Parameter(Mandatory=$True)] 
        $SObject,
        [Parameter(Mandatory=$True)] 
        $DObject
        )
        
        [array]$Values = $null

        Foreach ($Attribute in $AttributeMapping) {
            
            $BusinessRuleResult = "Nothing"
            If ($Attribute.BusinessRules) {

                Foreach ($BusinessRule in $Attribute.BusinessRules) {
                    If ($BusinessRule.Operator -eq ">") {
                        If ($SObject.($BusinessRule.SourceAttr)) {
                            If ($SObject.($BusinessRule.SourceAttr) -lt $BusinessRule.Value) { 
                                $BusinessRuleResult = $BusinessRule.Result
                                }
                            }
                        }
                    If ($BusinessRule.Operator -eq "<") {
                        If ($SObject.($BusinessRule.SourceAttr)) {
                            If ($SObject.($BusinessRule.SourceAttr) -gt $BusinessRule.Value) { 
                                $BusinessRuleResult = $BusinessRule.Result
                                }
                            }
                        }
                    If ($BusinessRule.Operator -eq "=") {
                        If ($SObject.($BusinessRule.SourceAttr)) {
                            If ($SObject.($BusinessRule.SourceAttr) -eq $BusinessRule.Value) { 
                                $BusinessRuleResult = $BusinessRule.Result
                                }
                            }
                        }
                    }
                }
                
            #If the business rules have given a result
            If ($BusinessRuleResult -ne "Nothing") {
                If ($BusinessRuleResult -ne $DObject.($Attribute.DestAttr)) {
                    $Value = "" | Select-Object -Property Attr,AttrValue
                    $Value.Attr = $Attribute.DestAttr
                    $Value.AttrValue = $BusinessRuleResult
                    $Values += $Value
                    }
                }

            #Else just check source with destination
            Elseif ($DObject.($Attribute.DestAttr) -ne $SObject.($Attribute.SourceAttr)) {
                
                $Value = "" | Select-Object -Property Attr,AttrValue
                $Value.Attr = $Attribute.DestAttr
                $Value.AttrValue = $SObject.($Attribute.SourceAttr)
                $Values += $Value
                }
            }
            
        Return $Values
    }

Function Sync-AADAccount2SharePoint {
    Param (
        [Parameter(Mandatory=$True)] 
        [Boolean]$EnableAdds,
        [Parameter(Mandatory=$True)] 
        [Boolean]$EnableUpdates,
        [Parameter(Mandatory=$True)] 
        [Boolean]$EnableDeletes,
        
        [Parameter(Mandatory=$True)] 
        [String]$SharePointListID, 
        [Parameter(Mandatory=$True)] 
        [String]$AzureADUserTypeAttribute,
        [Parameter(Mandatory=$True)] 
        [String]$AzureADUserType,

        [Parameter(Mandatory=$True)] 
        [array]$AttributeMapping
        )

    #Get Data
    $Employees = Get-PnPListItem -List $SharePointListID 
    $Accounts = Get-AzureADUser -All $True | Where-Object {$_.($AzureADUserTypeAttribute) -eq $AzureADUserType}

    #Sync from AAD to SharePointList
    $Diff = Find-SyncJoins -Slist $Accounts -Dlist $Employees.FieldValues -SAnchor "ObjectID" -DAnchor "AzureID"  

    #Add users to SharePointList
    If ($EnableAdds) {
        foreach ($Add in $Diff.InSourceNotDestionation) {
            New-SharePointListItem -SharePointListID $SharePointListID -Add $Add -AttributeMapping $AttributeMapping
            }
        }

    #Update from AAD to SharePointList
    If ($EnableUpdates) {
        Foreach ($Update in $Diff.InBothSourceAndDestionation) {
            
            $UpdateValues = Find-SyncObjectDiffSharePoint -AttributeMapping $AttributeMapping -Update $Update
                               
            If ($UpdateValues.Count -gt 0) {
                $SyncLogMessage = "Sync updated the followings Fields: " + $UpdateValues.Keys + " With the following values: " + $UpdateValues.Values
                $UpdateValues +=@{"SyncLog"=$SyncLogMessage}
                
                Set-PnPListItem -List $SharePointListID -Identity $Update.DItem.ID -Values $UpdateValues
                Write-Host "Succesfully Updated SharePoint Item: " $Update.DItem.Title " with " $SyncLogMessage -ForegroundColor Yellow
                
                }
            }
        }

    #Delete from SharePointList
    If ($EnableDeletes) {
        Foreach ($Delete in $Diff.InDestionationNotSource) {
            Remove-PnPListItem -List $SharePointListID -Identity $Delete.ID -Force
            Write-Host "Removed item from SharePoint list." -ForegroundColor Yellow
            }
        }
            
    }

Function Sync-AADSystemrole2SharePoint {
    Param (
        [Parameter(Mandatory=$True)] 
        [Boolean]$EnableAdds,
        [Parameter(Mandatory=$True)] 
        [Boolean]$EnableUpdates,
        [Parameter(Mandatory=$True)] 
        [Boolean]$EnableDeletes,
        
        [Parameter(Mandatory=$True)] 
        [String]$SharePointSystemRolesListID, 
        
        [Parameter(Mandatory=$True)] 
        [array]$AttributeMapping
        )

    #Get Data
    $SystemRoles = Get-PnPListItem -List $SharePointSystemRolesListID 
    $SystemRoles = $SystemRoles | Where-Object {$_.FieldValues.ProvisioningSystem -eq "AzureAD"}

    $Groups = Get-AzureADGroup -All $True
    
    #Sync from AAD to SharePointList
    $Diff = Find-SyncJoins -Slist $Groups -Dlist $SystemRoles.FieldValues -SAnchor "ObjectID" -DAnchor "AzureID"  

    #Add users to SharePointList
    If ($EnableAdds) {
        foreach ($Add in $Diff.InSourceNotDestionation) {
            New-SharePointListItem -SharePointListID $SharePointSystemRolesListID -Add $Add -AttributeMapping $AttributeMapping
            }
        }

    #Update from AAD to SharePointList
    If ($EnableUpdates) {
        Foreach ($Update in $Diff.InBothSourceAndDestionation) {
            
            $UpdateValues = Find-SyncObjectDiffSharePoint -AttributeMapping $AttributeMapping -Update $Update
                               
            If ($UpdateValues.Count -gt 0) {
                $SyncLogMessage = "Sync updated the followings Fields: " + $UpdateValues.Keys + " With the following values: " + $UpdateValues.Values
                $UpdateValues +=@{"SyncLog"=$SyncLogMessage}
                
                Set-PnPListItem -List $SharePointSystemRolesListID -Identity $Update.DItem.ID -Values $UpdateValues
                Write-Host "Succesfully Updated SharePoint Item: " $Update.DItem.Title " with " $SyncLogMessage -ForegroundColor Yellow
                
                }
            }
        }

    #Delete from SharePointList
    If ($EnableDeletes) {
        Foreach ($Delete in $Diff.InDestionationNotSource) {
            Remove-PnPListItem -List $SharePointSystemRolesListID -Identity $Delete.ID -Force
            Write-Host "Removed item from SharePoint list." -ForegroundColor Yellow
            }
        }
            
    }

Function Sync-SharePointUser2AADAccount {
    Param (
        [Parameter(Mandatory=$True)] 
        [Boolean]$EnableAdds,
        [Parameter(Mandatory=$True)] 
        [Boolean]$EnableUpdates,
        [Parameter(Mandatory=$True)] 
        [Boolean]$EnableDeletes,
        
        [Parameter(Mandatory=$True)] 
        [String]$SharePointListID, 
        [Parameter(Mandatory=$True)] 
        [String]$AzureADUserTypeAttribute,
        [Parameter(Mandatory=$True)] 
        [String]$AzureADUserType,

        [Parameter(Mandatory=$True)] 
        [array]$AttributeMapping
        )
          
    #Get Data
    $Employees = Get-PnPListItem -List $SharePointListID
    $Accounts = Get-AzureADUser | Where-Object {$_.($AzureADUserTypeAttribute) -eq $AzureADUserType}
 
    #Sync from SharePointList to AzureAD
    $Diff = Find-SyncJoins -Slist $Employees.fieldvalues -Dlist $Accounts -SAnchor "AzureID" -DAnchor "ObjectID"
    
    #Add accounts to Azure AD
    If ($EnableAdds) {
        
        foreach ($Add in $Diff.InSourceNotDestionation) {
            
            $Command = New-AADUser -Add $Add -AttributeMapping $AttributeMapping
            $SyncLogMessage = "Sync added a new user to Azure AD. Using command: "+ $Command +"."

            $NewUser = Get-AzureADUser -objectID $Add.Title
            Set-PnPListItem -List $SharePointListID -Identity $Add.ID -Values @{"SyncLog"=$SyncLogMessage;"AzureID"=$NewUser.objectID}
            Write-Host $SyncLogMessage        
            <#Send the an e-mail with the password
             
            $MessageBody = "<HTML>Dear Manager,<P>A new user ($UPN) was created that will report to you. Please provide the user with this password: $Password .<P>Kind regards, The IAM Automation Robot.."
            $MessageSubject = "New useraccount ($UPN) is ready for use."
                 
            #Get Manager Email addres to send new user email to.
            $ManagerTitle = $add.Manager.LookupValue
            $Manager = $Employees | Where-Object {$_.fieldvalues.Title -eq $ManagerTitle}
            $MailTo = $Manager.FieldValues.Email
 
            Try {
                Send-Email -MessageBody $MessageBody -MessageSubject $MessageSubject -MessageTo $MailTo -MessageFrom $MessageFrom -AzureO365Credential $MailboxCredential
                }
            Catch {
                $ListItem = $Employees | Where-Object {$_.fieldvalues.Title -eq $Add.Title}
                Set-PnPListItem -List $SharePointListID -Identity $ListItem.Id -Values @{"SyncLog"=$Error}
                }
                 
            $ListItem = $Employees | Where-Object {$_.fieldvalues.Title -eq $Add.Title}
            $SyncLogMessage = "Sending email message: SendFrom: "+ $MessageFrom + " SendTo: " + $MailTo + " Subject: " + $MessageSubject
            Set-PnPListItem -List $SharePointListID -Identity $ListItem.Id -Values @{"SyncLog"=$SyncLogMessage}
            #>


            }
        }

    #Update accounts in Azure AD
    If ($EnableUpdates) {
        Foreach ($Update in $Diff.InBothSourceAndDestionation) {
            
            $UpdateValues = Find-SyncObjectDiffAAD -AttributeMapping $AttributeMapping -SObject $Update.SItem -DObject $Update.DItem
            
            If ($UpdateValues) {
                $Command = Invoke-AADUpdate -ObjectID $Update.DItem.ObjectId -UpdateValues $UpdateValues
                $SyncLogMessage = "Sync Updated Azure AD. Using command: "+ $Command.ToString() +"."
                Set-PnPListItem -List $SharePointListID -Identity $Update.SItem.ID -Values @{"SyncLog"=$SyncLogMessage}
                Write-Host $SyncLogMessage
                }
                                            
            }
        }

    #Delte accounts in Azure AD
    If ($EnableDeletes) {
        Foreach ($Delete in $Diff.InDestionationNotSource) {
            Remove-AzureADUser -ObjectId $Delete.ObjectId
            }
        }
    }
    
Function Invoke-AADUpdate {
    param (
    [Parameter(Mandatory=$True)] 
    [String]$ObjectID,
    [Parameter(Mandatory=$True)] 
    [Array]$UpdateValues)    
        
    [string]$PSCommmandStr = 'Set-AzureADUser -ObjectId ' + $ObjectID + ' '
                    
    foreach ($Value in $UpdateValues) {
        
        $Attribute = $Value.Attr -replace '-'

        If ($Value.AttrValue.length -gt 0) {
            #To protect against command injection..
            If (($Value.AttrValue.GetType()).Name -eq "String") {
                $AttributeValue = $Value.AttrValue -replace '"' -replace "'" -replace '`'
                $PSCommmandStr += '-'+$Attribute+' "'+$AttributeValue+'" '
                }
            ElseIF (($Value.AttrValue.GetType()).Name -eq "Boolean") {
                $AttributeValue = '$'+$Value.AttrValue
                $PSCommmandStr += '-'+$Attribute+' '+$AttributeValue+' '
                }
            Else {
                $AttributeValue = $Value.AttrValue
                $PSCommmandStr += '-'+$Attribute+' '+$AttributeValue+' '
                }
            }
        Else {
            #Disabled because writing $null is currently not supported by the PowerShell CMDlets created by Microsoft.
            #$PSCommmandStr += '-'+$Attribute+' '+'$null'+' '
            }
        }
    
    #Avoid running a command with only Set-AzureADUser -ObjectID ... without any actual updates.
    If ($PSCommmandStr.Length -gt 65) {                
        $Command = [scriptblock]::create($PSCommmandStr)
        Invoke-Command -scriptblock $Command
        Return $Command
        }

}

Function New-AADUser {
    Param(
        [Parameter(Mandatory=$True)] 
        [Array]$Add,
        [Parameter(Mandatory=$True)] 
        [Array]$AttributeMapping
        )
    
    If (!($Add.Title.Contains("@"))) {
        Write-Host "Can't create user.. Title invallid."
        break
        }
    
    
    $UPN = $Add.Title
    $MailNick = ($UPN.Split("@"))[0]
    $Password = GET-Temppassword
    $passwordProfile = Get-PasswordProfile -Password $Password
    
    [string]$PSCommmandStr = 'New-AzureADUser -UserPrincipalName '+$UPN+' -PasswordProfile $passwordProfile -UserType Member -MailNickName '+$MailNick+' -State Account -AccountEnabled $True '

    [array]$Values = $null
    Foreach ($Attribute in $AttributeMapping) {
        If ($Add.($Attribute.SourceAttr)){
            
            $AttributeName = $Attribute.DestAttr -replace '-'
            $AttributeValue = $Add.($Attribute.SourceAttr)
            
            #To protect against command injection..
            If (($AttributeValue.GetType()).Name -eq "String") {
                $AttributeValue = $AttributeValue -replace '"' -replace "'" -replace '`'
                $PSCommmandStr += '-'+$AttributeName+' "'+$AttributeValue+'" '
                }
            ElseIF (($AttributeValue.GetType()).Name -eq "Boolean") {
                $AttributeValue = '$'+$AttributeValue
                $PSCommmandStr += '-'+$AttributeName+' '+$AttributeValue+' '
                }
            Else {
                $PSCommmandStr += '-'+$AttributeName+' '+$AttributeValue+' '
                }
        }
        }
                           
    $Command = [scriptblock]::create($PSCommmandStr)
    Try {
        Invoke-Command -scriptblock $Command
        }
    Catch {
        Throw
        }
    Return $PSCommmandStr        
    
    }

Function Add-UserSystemRoletoAzureAD {
        Param (
        [Parameter(Mandatory=$True)] 
        [String]$SharePointEmployeesListID, 
        [Parameter(Mandatory=$True)] 
        [String]$SharePointSystemRolesListID
        )

    #Get Data
    $Employees = Get-PnPListItem -List $SharePointEmployeesListID 
    $SystemRoles = Get-PnPListItem -List $SharePointSystemRolesListID 
    

    Foreach ($Employee in $Employees) {
        
        Foreach ($Add in $Employee.Fieldvalues.ComputedSRDiffAdds) {
            $SystemRole = $SystemRoles | Where-Object {$_.Id -eq $Add.LookupId}
            
            Try {
                Add-AzureADGroupMember -ObjectId ($SystemRole.FieldValues.AzureID) -RefObjectId ($Employee.FieldValues.AzureID)
                }
            Catch {
                Throw
                }

            $Message = "Added user: "+ $Employee.FieldValues.Title + " to " + $SystemRole.FieldValues.Title
            Write-Host $Message
            }
        
        }

    }

Function Remove-UserSystemRoletoAzureAD {
        Param (
        [Parameter(Mandatory=$True)] 
        [String]$SharePointEmployeesListID, 
        [Parameter(Mandatory=$True)] 
        [String]$SharePointSystemRolesListID
        )

    #Get Data
    $Employees = Get-PnPListItem -List $SharePointEmployeesListID 
    $SystemRoles = Get-PnPListItem -List $SharePointSystemRolesListID 
    

    Foreach ($Employee in $Employees) {
        
        Foreach ($Remove in $Employee.Fieldvalues.ComputedSRDiffRemoves) {
            $SystemRole = $SystemRoles | Where-Object {$_.Id -eq $Remove.LookupId}
            
            Try {
                Remove-AzureADGroupMember -ObjectId ($SystemRole.FieldValues.AzureID) -MemberId ($Employee.FieldValues.AzureID)
                }
            Catch {
                Throw
                }

            $Message = "Removed user: "+ $Employee.FieldValues.Title + " from " + $SystemRole.FieldValues.Title
            Write-Host $Message
            }
        
        }

    }

#######################################################################################################
#Assert and Link Functions

Function Assert-SharePointSystemRole {
    Param (
        [Parameter(Mandatory=$True)] 
        [String]$SharePointSystemRolesListID,
        [Parameter(Mandatory=$True)] 
        [String]$SharePointEmployeesListID,
        [Parameter(Mandatory=$True)] 
        [String]$SharePointInformationSystemsListID,
        [Parameter(Mandatory=$True)] 
        [String]$SharePointBusinessRolesListID
        )

    #Assert ComputedLinkedBusinessRoles
    Assert-SharePointSystemRoleComputedLinkedBusinessRoles -SharePointSystemRolesListID $SharePointSystemRolesListID -SharePointBusinessRolesListID $SharePointBusinessRolesListID
    
    #Assert ComputedExternalAllowed
    Assert-SharePointSystemRoleComputedExternalAllowed -SharePointSystemRolesListID $SharePointSystemRolesListID -SharePointBusinessRolesListID $SharePointBusinessRolesListID -SharePointInformationSystemsListID $SharePointInformationSystemsListID

    #Assert ComputedMembers (All members.. allowed or not..)
    Assert-SharePointSystemRoleComputedMembers -SharePointSystemRolesListID $SharePointSystemRolesListID -SharePointBusinessRolesListID $SharePointBusinessRolesListID -SharePointEmployeesListID $SharePointEmployeesListID

    #Assert ComputedMembersEffective (All members that should actualy be member)
    Assert-SharePointSystemRoleComputedMembersEffective -SharePointSystemRolesListID $SharePointSystemRolesListID -SharePointEmployeesListID $SharePointEmployeesListID

    
    }

Function Assert-SharePointSystemRoleComputedLinkedBusinessRoles {
    Param (
        [Parameter(Mandatory=$True)] 
        [String]$SharePointSystemRolesListID,
        [Parameter(Mandatory=$True)] 
        [String]$SharePointBusinessRolesListID
        )
    
    $SystemRoles = Get-PnPListItem -List $SharePointSystemRolesListID
    $BusinessRoles = Get-PnPListItem -List $SharePointBusinessRolesListID

    Foreach ($SystemRole in $SystemRoles) {
        
        $UpdateComputedLinkedBusinessRoles = $False
        $IST_LinkedBusinessRoles = $SystemRole.FieldValues.ComputedLinkedBusinessRoles
        $SOL_LinkedBusinessRoles = $BusinessRoles | Where-Object {$_.FieldValues.SystemRoles.LookupID -eq $SystemRole.Id}
        
        #The LinkedBusinessRoles should not be empty.
        if ($SOL_LinkedBusinessRoles.count -gt 0) {
            
            #If it currenlty is empty.. then we can set it without worring about doing updates.
            If ($IST_LinkedBusinessRoles.count -eq 0) {
                $UpdateComputedLinkedBusinessRoles = $True
                }
        
            #Both IST and SOLL have data. There might be an update in the list.
            Else {
                $Diff = Find-SyncJoins -SList $IST_LinkedBusinessRoles -DList $SOL_LinkedBusinessRoles -SAnchor "LookupId" -DAnchor "Id"

                #If we have either adds or removes.
                If (($Diff.InDestionationNotSource -ne $null) -or ($Diff.InSourceNotDestionation -ne $null)) {
                    $UpdateComputedLinkedBusinessRoles = $True
                    }
                }

            }
        #The LinkedBusinessRoles contains values but should not.
        Else {
            If ($IST_LinkedBusinessRoles -ne $null) {
                #Clear the list
                Set-PnPListItem -List $SharePointSystemRolesListID -Identity $SystemRole.Id -Values @{ComputedLinkedBusinessRoles=0;}
                }
            }

        If ($UpdateComputedLinkedBusinessRoles -eq $True){
            
            #Build a comma sperated list of Ids to add to sharepoint.
            [string]$Value = $null
            [string]$Names = $null
                    
            Foreach ($SOL_LinkedBusinessRole in $SOL_LinkedBusinessRoles) {
                $Value += $SOL_LinkedBusinessRole.Id.ToString() + ","
                $Names += ($BusinessRoles | Where-Object {$_.Id -eq $SOL_LinkedBusinessRole.Id}).FieldValues.Title + " ,"
                }
            $Value = $Value.Substring(0,($Value.Length -1))
            $Names = $Names.Substring(0,($Names.Length -2))
                    
            $SyncLogMessage = "Assert-SharePointSystemRole found that the following Linked BusinessRoles were no yet linked (And linked them): " + $Names
            Set-PnPListItem -List $SharePointSystemRolesListID -Identity $SystemRole.Id -Values @{ComputedLinkedBusinessRoles=$Value;SyncLog=$SyncLogMessage;}
            Write-Host $SyncLogMessage
            }


        }
    }

Function Assert-SharePointSystemRoleComputedExternalAllowed {
    Param (
        [Parameter(Mandatory=$True)] 
        [String]$SharePointInformationSystemsListID,
        [Parameter(Mandatory=$True)] 
        [String]$SharePointBusinessRolesListID,
        [Parameter(Mandatory=$True)] 
        [String]$SharePointSystemRolesListID
        )
    
    $SystemRoles = Get-PnPListItem -List $SharePointSystemRolesListID
    $BusinessRoles = Get-PnPListItem -List $SharePointBusinessRolesListID
    $InformationSystems = Get-PnPListItem -List $SharePointInformationSystemsListID 

    Foreach ($SystemRole in $SystemRoles) {
        
        #True unless 1 of the Informationsystems of Businessroles has it set to false
        $ComputedExternalAllowed = $True
        $SyncLogMessageReson ="Updated because default value is: True"

        #Check the value on the linked Information System.
        If ($SystemRole.Fieldvalues.InformationSystem) {
            $LinkedInformationSystemID = $SystemRole.Fieldvalues.InformationSystem.LookupId
            $LinkedInformationSystem = $InformationSystems | Where-Object {$_.id -eq $LinkedInformationSystemID}
            if ($LinkedInformationSystem -ne $null) {
                If ($LinkedInformationSystem.Fieldvalues.AllowExternalAccess -eq $False) {
                    $ComputedExternalAllowed = $False
                    $SyncLogMessageReson = "Updated because of the value on information system: " + $LinkedInformationSystem.Fieldvalues.Title
                    }
                }
            }
        
        #Check the value on the businessrole only if the value is not alreay set to false based on the information system.
        If ($ComputedExternalAllowed -eq $True) {
            Foreach ($BusinessRole in $BusinessRoles) {
                If ($BusinessRole.Fieldvalues.AllowExternalAccess -eq $False) {
                    $ComputedExternalAllowed = $False
                    $SyncLogMessageReson = "Updated because of the value on business role: " + $BusinessRole.Fieldvalues.Title
                    }
            
                }
            }
        
        #Check if the new value matches the current value
        $Current_ComputedExternalAllowed = $SystemRole.Fieldvalues.ComputedExternalAllowed
        If ($Current_ComputedExternalAllowed -ne $ComputedExternalAllowed) {
            
            $SyncLogMessage = "Sync detected update in ComputedExternalAllowed value set to: " + $ComputedExternalAllowed + ". With the following reson: " + $SyncLogMessageReson

            Set-PnPListItem -List $SharePointSystemRolesListID -Identity $SystemRole.Id -Values @{ComputedExternalAllowed=$ComputedExternalAllowed;SyncLog=$SyncLogMessage}    
            Write-Host $SyncLogMessage
            }
        }
    }

Function Assert-SharePointSystemRoleComputedMembers {
    Param (
        [Parameter(Mandatory=$True)] 
        [String]$SharePointSystemRolesListID,
        [Parameter(Mandatory=$True)] 
        [String]$SharePointBusinessRolesListID,
        [Parameter(Mandatory=$True)] 
        [String]$SharePointEmployeesListID
        )
    
    $SystemRoles = Get-PnPListItem -List $SharePointSystemRolesListID
    $BusinessRoles = Get-PnPListItem -List $SharePointBusinessRolesListID
    $Employees = Get-PnPListItem -List $SharePointEmployeesListID -Fields Id,Title,PersonalSystemRoles,BusinessRoles
    
    Foreach ($SystemRole in $SystemRoles) {
        
        $WriteCombinedMembers = $False
        
        [array]$CombinedMembers = $null
        #Get All LinkedMembers via the Personal System Role (Employee.PersonalSystemRoles > SystemRole)
        $Employeeslinked_PersonalSystemRoles = $Employees | Where-Object {$_.FieldValues.PersonalSystemRoles.LookupID -eq $SystemRole.Id}
        
        #Get All LinkedMembers via the BusinessRoles (Employee.BusinessRoles.SystemRoles > SystemRole)
        [array]$Employeeslinked_BusinessRole = $null
        $ComputedLinkedBusinessRoles = $BusinessRoles | Where-Object {$_.FieldValues.SystemRoles.LookupID -eq $SystemRole.Id}
        Foreach ($ComputedLinkedBusinessRole in $ComputedLinkedBusinessRoles) {
            $Employeeslinked_BusinessRole += $Employees | Where-Object {$_.FieldValues.BusinessRoles.LookupID -eq $ComputedLinkedBusinessRole.Id} 
            }
        
        #Create a deduplicated list of members.
        $CombinedMembers += $Employeeslinked_PersonalSystemRoles
        $CombinedMembers += $Employeeslinked_BusinessRole | Where-Object {$_.Id -notin $Employeeslinked_PersonalSystemRoles.Id}

        #Get CurrentComputedMembers if any
        $CurrentComputedMembers = $null
        If ($SystemRole.FieldValues.ComputedMembers) {
            $CurrentComputedMembers = $SystemRole.FieldValues.ComputedMembers
            }
        
        If ($CombinedMembers.Count -gt 0) {
            #If there are current members.. check if update is nessarcy.
            If  ($CurrentComputedMembers.count -gt 0) {
                $Diff = Find-SyncJoins -SList $CurrentComputedMembers -DList $CombinedMembers -SAnchor "LookupID" -DAnchor "Id"

                #If we have either adds or removes.
                If (($Diff.InDestionationNotSource -ne $null) -or ($Diff.InSourceNotDestionation -ne $null)) {
                    $WriteCombinedMembers = $True
                    }
                }
            #Otherwise just overwrite.
            Else {
                $WriteCombinedMembers = $True
                }
            }
        ElseIF ($CurrentComputedMembers.count -gt 0) {
            $SyncLogMessage = "Assert-SharePointSystemRoleComputedMembers updates ComputedMembers to empty"
            Set-PnPListItem -List $SharePointSystemRolesListID -Identity $SystemRole.Id -Values @{SyncLog=$SyncLogMessage;ComputedMembers=0;}
            Write-Host $SyncLogMessage
            }
        
        If ($WriteCombinedMembers -eq $True) {
            
            #Build a comma sperated list of Ids to add to sharepoint.
            [string]$Value = $null
            [string]$Names = $null
                 
            Foreach ($CombinedMember in $CombinedMembers) {
                $Value += $CombinedMember.Id.ToString() + ","
                $Names += $CombinedMember.FieldValues.Title + " ,"
                }
            
            $Value = $Value.Substring(0,($Value.Length -1))
            $Names = $Names.Substring(0,($Names.Length -2))
                    
            $SyncLogMessage = "Assert-SharePointSystemRoleComputedMembers updates ComputedMembers to: " + $Names
            Set-PnPListItem -List $SharePointSystemRolesListID -Identity $SystemRole.Id -Values @{ComputedMembers=$Value;SyncLog=$SyncLogMessage}
            Write-Host $SyncLogMessage
            }
            
        }
    }

Function Assert-SharePointSystemRoleComputedMembersRemovedBecauseNoExternal {
    Param (
        [Parameter(Mandatory=$True)] 
        [String]$SharePointSystemRolesListID,
        [Parameter(Mandatory=$True)] 
        [String]$SharePointEmployeesListID
        )
    
    $SystemRoles = Get-PnPListItem -List $SharePointSystemRolesListID 
    $Employees = Get-PnPListItem -List $SharePointEmployeesListID -Fields Id,Title,ComputedMembers,EmployeeType
    
    Foreach ($SystemRole in $SystemRoles) {
        
        $WriteComputedMembersRemovedBecauseNoExternal = $False
        [array]$ComputedMembersRemovedBecauseNoExternal = $null

        #Do this eveluation only if this SystemRole is only for Internals
        If ($SystemRole.FieldValues.ComputedExternalAllowed -eq $False) {
            
            #And if there are any computed members.
            If ($SystemRole.FieldValues.ComputedMembers.count -gt 0) {
            
                #Get Employee Object behind ComputedMembers
                $ComputedMembers = $Employees | Where-Object {$SystemRole.FieldValues.ComputedMembers.LookupId -eq $_.Id}
            
                #Check if the EmployeeObject is of type guest.
                Foreach ($ComputedMember in $ComputedMembers) {
                
                    #If not add it to the list.
                    If ($ComputedMember.FieldValues.EmployeeType -eq "Guest") {
                        $ComputedMembersRemovedBecauseNoExternal += $ComputedMember 
                        }
                
                    } 
                                
                IF ($SystemRole.FieldValues.ComputedMembersRemovedBecauseNoE) {
                    
                    #If removed because of ComputedMembersRemovedBecauseNoAdmin contains any users
                    If ($ComputedMembersRemovedBecauseNoExternal.count -gt 0) {
                        
                        $Diff = Find-SyncJoins -SList $SystemRole.FieldValues.ComputedMembersRemovedBecauseNoE -DList $ComputedMembersRemovedBecauseNoExternal -SAnchor "LookupID" -DAnchor "Id"

                        #If we have either adds or removes.
                        If (($Diff.InDestionationNotSource -ne $null) -or ($Diff.InSourceNotDestionation -ne $null)) {
                            $WriteComputedMembersRemovedBecauseNoExternal = $True
                            }
                        }
                    Else {
                        $SyncLogMessage = "Assert-SharePointSystemRoleComputedMembersRemovedBecauseNoExternal updates ComputedMembersRemovedBecauseNoExternal to empty."
                        Set-PnPListItem -List $SharePointSystemRolesListID -Identity $SystemRole.Id -Values @{SyncLog=$SyncLogMessage;ComputedMembersRemovedBecauseNoE=0;}
                        Write-Host $SyncLogMessage
                        }
                    }
                Else{
                    If ($ComputedMembersRemovedBecauseNoExternal.count -gt 0) {
                        $WriteComputedMembersRemovedBecauseNoExternal = $True
                        }
                    }

                }
            #If no computed members there can also not be ComputedMembersRemovedBecauseNoExternal
            Else {
                If ($SystemRole.FieldValues.ComputedMembersRemovedBecauseNoE.count -gt 0) {
                    $SyncLogMessage = "Assert-SharePointSystemRoleComputedMembersRemovedBecauseNoExternal updates ComputedMembersRemovedBecauseNoExternal to empty, because there are no ComputedMembers"
                    Set-PnPListItem -List $SharePointSystemRolesListID -Identity $SystemRole.Id -Values @{SyncLog=$SyncLogMessage;ComputedMembersRemovedBecauseNoE=0;}
                    Write-Host $SyncLogMessage
                    }
                }
            }

        #If not only for admins.. make sure the the ComputedMembersRemovedBecauseNoAdmin is empty
        Else {
            If ($SystemRole.FieldValues.ComputedMembersRemovedBecauseNoE.count -gt 0) {
                $SyncLogMessage = "Assert-SharePointSystemRoleComputedMembersRemovedBecauseNoExternal updates ComputedMembersRemovedBecauseNoExternal to empty, because ComputedExternalAllowed is set to False"
                Set-PnPListItem -List $SharePointSystemRolesListID -Identity $SystemRole.Id -Values @{SyncLog=$SyncLogMessage;ComputedMembersRemovedBecauseNoE=0}
                Write-Host $SyncLogMessage
                }
            }

        If ($WriteComputedMembersRemovedBecauseNoExternal -eq $True) {
            #Build a comma sperated list of Ids to add to sharepoint.
            [string]$Value = $null
            [string]$Names = $null
                 
            Foreach ($ComputedMemberRemovedBecauseNoExternal in $ComputedMembersRemovedBecauseNoExternal) {
                $Value += $ComputedMemberRemovedBecauseNoExternal.Id.ToString() + ","
                $Names += $ComputedMemberRemovedBecauseNoExternal.FieldValues.Title + " ,"
                }
            
            $Value = $Value.Substring(0,($Value.Length -1))
            $Names = $Names.Substring(0,($Names.Length -2))
                    
            $SyncLogMessage = "Assert-SharePointSystemRoleComputedMembersRemovedBecauseNoExternal updates ComputedMembersRemovedBecauseNoExternal to: " + $Names
            Set-PnPListItem -List $SharePointSystemRolesListID -Identity $SystemRole.Id -Values @{ComputedMembersRemovedBecauseNoE=$Value;SyncLog=$SyncLogMessage}
            Write-Host $SyncLogMessage
            }

        }
    }

Function Assert-SharePointSystemRoleComputedMembersEffective {
    Param (
        [Parameter(Mandatory=$True)] 
        [String]$SharePointSystemRolesListID,
        [Parameter(Mandatory=$True)] 
        [String]$SharePointEmployeesListID
        )
    
    #Assert ComputedMembersRemovedBecauseNoAdmin
    Assert-SharePointSystemRoleComputedMembersRemovedBecauseNoExternal -SharePointSystemRolesListID $SharePointSystemRolesListID -SharePointEmployeesListID $SharePointEmployeesListID
        
    $SystemRoles = Get-PnPListItem -List $SharePointSystemRolesListID 
    $Employees = Get-PnPListItem -List $SharePointEmployeesListID -Fields Id,Title
    
    Foreach ($SystemRole in $SystemRoles) {
        
        $ClearComputedMembersEffective = $False

        #Check to see if there are Computed Members
        If ($SystemRole.FieldValues.ComputedMembers) {
            
            [array]$ComputedMembersEffectiveLookup = $null
            $ComputedMembersEffectiveLookup = $SystemRole.FieldValues.ComputedMembers
            
            [Array]$ComputedMembersEffective = $null
            Foreach ($ComputedMemberEffectiveLookup in $ComputedMembersEffectiveLookup) {
                $ComputedMembersEffective += $Employees | Where-Object {$_.Id -eq $ComputedMemberEffectiveLookup.LookupId}
                }

            #If there are members that should not be member because they don't have an admin account. Then remove them from effective Members
            If ($SystemRole.FieldValues.ComputedMembersRemovedBecauseNoA.Count -gt 0) {
                $ComputedMembersEffective = $ComputedMembersEffective | Where-Object {$_.Id -notin $SystemRole.FieldValues.ComputedMembersRemovedBecauseNoA.LookupId}
                }
            
            #If there are members that should not be member because they are external. Then remove them from effective Members
            If ($SystemRole.FieldValues.ComputedMembersRemovedBecauseNoE.Count -gt 0) {
                $ComputedMembersEffective = $ComputedMembersEffective | Where-Object {$_.Id -notin $SystemRole.FieldValues.ComputedMembersRemovedBecauseNoE.LookupId}
                }

            #if there are any members remaining after removing the wrong ones.
            If ($ComputedMembersEffective.count -gt 0) {
                
                #Check if an update to sharepoint is nessarcy.
                $Diff = Find-SyncJoins -SList $ComputedMembersEffective  -DList $SystemRole.FieldValues.ComputedMembersEffective  -SAnchor "Id" -DAnchor "LookupId"

                #If we have either adds or removes.
                If (($Diff.InDestionationNotSource -ne $null) -or ($Diff.InSourceNotDestionation -ne $null)) {
                    
                    #Build a comma sperated list of Ids to add to sharepoint.
                    [string]$Value = $null
                    [string]$Names = $null
                 
                    Foreach ($ComputedMemberEffective in $ComputedMembersEffective) {
                        $Value += $ComputedMemberEffective.Id.ToString() + ","
                        $Names += $ComputedMemberEffective.FieldValues.Title + " ,"
                        }
            
                    $Value = $Value.Substring(0,($Value.Length -1))
                    $Names = $Names.Substring(0,($Names.Length -2))
                    
                    $SyncLogMessage = "Assert-SharePointSystemRoleComputedMembersEffective updates ComputedMembersEffective to: " + $Names
                    Set-PnPListItem -List $SharePointSystemRolesListID -Identity $SystemRole.Id -Values @{ComputedMembersEffective=$Value;SyncLog=$SyncLogMessage}
                    Write-Host $SyncLogMessage

                    }
                }

            Else {
                $ClearComputedMembersEffective = $True
                }
                
            }
        #No ComputedMembers means no Effective Members.
        Else {
            $ClearComputedMembersEffective = $True       
            }

        If ($ClearComputedMembersEffective -eq $True) {
            If ($SystemRole.FieldValues.ComputedMembersEffective) {
                $SyncLogMessage = "Assert-SharePointSystemRoleComputedMembersEffective updates ComputedMembersEffective to empty."
                Set-PnPListItem -List $SharePointSystemRolesListID -Identity $SystemRole.Id -Values @{SyncLog=$SyncLogMessage;ComputedMembersEffective=0;}
                Write-Host $SyncLogMessage
                }
            }
        }
    
    }

Function Assert-BacklinkSharePointMultivalueLookupAttribute {
    Param (
        [Parameter(Mandatory=$True)] 
        [String]$SourceListID,
        [Parameter(Mandatory=$True)] 
        [String]$SourceAttributeName,
        [Parameter(Mandatory=$True)] 
        [String]$DestinationListID,
        [Parameter(Mandatory=$True)] 
        [String]$DestinationAttributeName
        )


    $SourceList = Get-PnPListItem -List $SourceListID -Fields Id,Title,$SourceAttributeName
    $DestinationList = Get-PnPListItem -List $DestinationListID -Fields Id,Title,$DestinationAttributeName
    
    [Array]$UpdateList = $null

    #Set the IST in the UpdateList
    Foreach ($DestinationItem in $DestinationList) {
        
        $Update = "" | Select-Object -Property ID,SOLL,IST
        $Update.ID = $DestinationItem.Id
        $Update.SOLL = New-Object System.Collections.Generic.List[System.Object]
        If ($DestinationItem.FieldValues.$DestinationAttributeName) {
` $Update.IST = $DestinationItem.FieldValues.$DestinationAttributeName.LookupId}
        Else {
            $Update.IST = ""
            }

        $UpdateList += $Update
        }
    
    #Set the SOLL in the UpdateList
    Foreach ($SourceItem in $SourceList) {
        
        [array]$SourceAttributeValueObjects = $null
        IF($SourceItem.FieldValues.$SourceAttributeName) {
            
            #Get full member Objects
            $SourceAttributeValueIDs = $SourceItem.FieldValues.$SourceAttributeName
            Foreach ($SourceAttributeValueID in $SourceAttributeValueIDs) {
                $SourceAttributeValueObjects += $DestinationList | Where-Object {$_.Id -eq $SourceAttributeValueID.LookupId}
                }
            } 

        #look through the linked System Roles and update the SOLLList
        Foreach ($SourceAttributeValueObject in $SourceAttributeValueObjects) {
            
            #Find the right SOLLList Item
            [int]$Count = 0
            
            Foreach ($UpdateItem in $UpdateList) {
                
                If ($UpdateItem.ID -eq $SourceAttributeValueObject.ID) {
                    
                    $UpdateList[$Count].SOLL.Add($SourceItem.Id.ToString())
                    }
                
                $Count = $Count + 1
                }


        
            }
        
        }

    #Compare SOLL to IST and update.. if nessecary.
    Foreach ($UpdateItem in $UpdateList) {
        
        [string]$SOLLValue = $null
        If ($UpdateItem.SOLL) {
            
            #Build a comma sperated list of Ids so we can use it to update the sharepoint list
            Foreach ($SOLLItem in $UpdateItem.SOLL) {
                $SOLLValue += $SOLLItem + ","
                }
            
            $SOLLValue = $SOLLValue.Substring(0,($SOLLValue.Length -1)).Replace(" ","")
            }
        
        [string]$ISTValue = $null
        If ($UpdateItem.IST) {
            
            #Build a comma sperated list of Ids so we can use it to update the sharepoint list
            Foreach ($ISTItem in $UpdateItem.IST) {
                $ISTValue += $ISTItem.ToString() + ","
                }
            
            $ISTValue = $ISTValue.Substring(0,($ISTValue.Length -1)).Replace(" ","")
            }           

        If ($SOLLValue -ne $ISTValue){

            $SyncLogMessage = "Sync updated the $DestinationAttributeName to " + $SOLLValue
            Set-PnPListItem -List $DestinationListID -Identity $UpdateItem.ID -Values @{$DestinationAttributeName=IF($SOLLValue){$SOLLValue}else{0};SyncLog=$SyncLogMessage}
            Write-Host $SyncLogMessage    
   
            }
        }
        
    }

Function Get-CurrentSRInDirectory {
        Param (
        [Parameter(Mandatory=$True)] 
        [String]$SharePointSystemRolesListID,
        [Parameter(Mandatory=$True)] 
        [String]$SharePointEmployeesListID,
        [Parameter(Mandatory=$True)] 
        [String]$SharePointEmployeeAttribute
        )

    
    $Employees = Get-PnPListItem -List $SharePointEmployeesListID
    $SystemRoles = Get-PnPListItem -List $SharePointSystemRolesListID | Where-Object {$_.Fieldvalues.ProvisioningSystem -eq "AzureAD"}

    Foreach ($Employee in $Employees) {
        
        $AADGroupMemberships = Get-AzureADUserMembership -ObjectId $Employee.FieldValues.AzureID
        [Array]$SharePointSystemRoleMemberships = $null
        
        Foreach ($AADGroupMembership in $AADGroupMemberships) {
            $SharePointSystemRoleMemberships += $SystemRoles | Where-Object {$_.FieldValues.AzureID -eq $AADGroupMembership.ObjectID}
            }
        
        [string]$SOLLValue = $null
        if ($SharePointSystemRoleMemberships) {
            #Build a comma sperated list of Ids so we can use it to update the sharepoint list
            Foreach ($SharePointSystemRoleMembership in $SharePointSystemRoleMemberships) {
                $SOLLValue += $SharePointSystemRoleMembership.Id.ToString() + ","
                }
            
            $SOLLValue = $SOLLValue.Substring(0,($SOLLValue.Length -1)).Replace(" ","")
            
            }
        
        [string]$ISTValue = $null
        IF ($Employee.FieldValues.$SharePointEmployeeAttribute.count -gt 0) {

            Foreach ($SharePointSystemRoleMembership in $Employee.FieldValues.$SharePointEmployeeAttribute) {
                $ISTValue += $SharePointSystemRoleMembership.LookupId.ToString() + ","
                }
                
            $ISTValue = $ISTValue.Substring(0,($ISTValue.Length -1)).Replace(" ","")
            
            }
        
        If ($SOLLValue -ne $ISTValue) {
            $SyncLogMessage = "Sync updated the $SharePointEmployeeAttribute to " + $SOLLValue
            Set-PnPListItem -List $SharePointEmployeesListID -Identity $Employee.ID -Values @{$SharePointEmployeeAttribute=IF($SOLLValue){$SOLLValue}else{0};SyncLog=$SyncLogMessage}
            Write-Host $SyncLogMessage
            }

        }
    
    }

Function Assert-SharePointUserComputedSRDiff {`
  Param (
        [Parameter(Mandatory=$True)] 
        [String]$SharePointSystemRolesListID,
        [Parameter(Mandatory=$True)] 
        [String]$SharePointEmployeesListID
        )
    
    $Employees = Get-PnPListItem -List $SharePointEmployeesListID -Fields Id,Title,ComputedSREffective,CurrentSRInDirectory,ComputedSRDiffAdds,ComputedSRDiffRemoves
    $SystemRoles = Get-PnPListItem -List $SharePointSystemRolesListID
    
    Foreach ($Employee in $Employees) {
        
        $ComputedSREffective = $Employee.FieldValues.ComputedSREffective.LookupId | sort 
        $CurrentSRInDirectory = $Employee.FieldValues.CurrentSRInDirectory.LookupId | sort

        $ComputedSRDiffToBeAdded = $Employee.FieldValues.ComputedSRDiffAdds.LookupId | Sort
        $ComputedSRDiffToBeRemoved = $Employee.FieldValues.ComputedSRDiffRemoves.LookupId | Sort


        #Has only computed SystemRoles and no current SystemRoles
        If ($ComputedSREffective -and -not $CurrentSRInDirectory) {
            
            
            [string]$SOLValue = $null
            #Build a comma sperated list of Ids so we can use it to update the sharepoint list
            Foreach ($Item in $ComputedSREffective) {
                $SOLValue += $Item.ToString() + ","
                }
            $SOLValue = $SOLValue.Substring(0,($SOLValue.Length -1)).Replace(" ","")

            [string]$ISTValue = $null
            If ($ComputedSRDiffToBeAdded) {
                #Build a comma sperated list of Ids so we can use it to update the sharepoint list
                Foreach ($Item in $ComputedSRDiffToBeAdded) {
                    $ISTValue += $Item.ToString() + ","
                    }
                $ISTValue = $ISTValue.Substring(0,($ISTValue.Length -1)).Replace(" ","")
                }
            
            #Update only if nesccary.
            If ($SOLValue -ne $ISTValue) {
                $SyncLogMessage = "Sync updated the ComputedSRDiffAdds to " + $SOLValue
                Set-PnPListItem -List $SharePointEmployeesListID -Identity $Employee.ID -Values @{ComputedSRDiffAdds=IF($SOLValue){$SOLValue}else{0};ComputedSRDiffRemoves=0;SyncLog=$SyncLogMessage}
                Write-Host $SyncLogMessage
                } 
            }
                        
        #Has Onlye Current system Roles no Current Roles.
        If ($CurrentSRInDirectory -and -not $ComputedSREffective) {
            
            
            [string]$SOLValue = $null
            #Build a comma sperated list of Ids so we can use it to update the sharepoint list
            Foreach ($Item in $CurrentSRInDirectory) {
                $SOLValue += $Item.ToString() + ","
                }
            $SOLValue = $SOLValue.Substring(0,($SOLValue.Length -1)).Replace(" ","")

            [string]$ISTValue = $null
            If ($ComputedSRDiffToBeRemoved) {
                #Build a comma sperated list of Ids so we can use it to update the sharepoint list
                Foreach ($Item in $ComputedSRDiffToBeRemoved) {
                    $ISTValue += $Item.ToString() + ","
                    }
                $ISTValue = $ISTValue.Substring(0,($ISTValue.Length -1)).Replace(" ","")
                }
            
            #Update only if nesccary.
            If ($SOLValue -ne $ISTValue) {
                $SyncLogMessage = "Sync updated the ComputedSRDiffRemoves to " + $SOLValue
                Set-PnPListItem -List $SharePointEmployeesListID -Identity $Employee.ID -Values @{ComputedSRDiffRemoves=IF($SOLValue){$SOLValue}else{0};ComputedSRDiffAdds=0;SyncLog=$SyncLogMessage}
                Write-Host $SyncLogMessage
                } 
            }

        #Has Both
        If ($CurrentSRInDirectory -and $ComputedSREffective) {
            
            $Diff = Find-SyncJoins -SList $Employee.FieldValues.ComputedSREffective -DList $Employee.FieldValues.CurrentSRInDirectory -SAnchor "LookupId" -DAnchor "LookupId"

            #Contains adds
            [string]$SOLValue = $null
            if ($Diff.InSourceNotDestionation) {
                #Build a comma sperated list of Ids so we can use it to update the sharepoint list
                Foreach ($Item in $Diff.InSourceNotDestionation.LookupId | Sort) {
                    $SOLValue += $Item.ToString() + ","
                    }
                $SOLValue = $SOLValue.Substring(0,($SOLValue.Length -1)).Replace(" ","")
                }
            
            [string]$ISTValue = $null
            if ($ComputedSRDiffToBeAdded) {
                #Build a comma sperated list of Ids so we can use it to update the sharepoint list
                Foreach ($Item in $ComputedSRDiffToBeAdded | sort) {
                    $ISTValue += $Item.ToString() + ","
                    }
                $ISTValue = $ISTValue.Substring(0,($ISTValue.Length -1)).Replace(" ","")
                }

            #Update only if nesccary.
            If ($SOLValue -ne $ISTValue) {
                $SyncLogMessage = "Sync updated the ComputedSRDiffAdds to " + $SOLValue
                Set-PnPListItem -List $SharePointEmployeesListID -Identity $Employee.ID -Values @{ComputedSRDiffAdds=IF($SOLValue){$SOLValue}else{0};SyncLog=$SyncLogMessage}
                Write-Host $SyncLogMessage
                } 
            

            #Contains removes
            [string]$SOLValue = $null
            If ($Diff.InDestionationNotSource) {
                            
                #Build a comma sperated list of Ids so we can use it to update the sharepoint list
                Foreach ($Item in $Diff.InDestionationNotSource.LookupId | sort) {
                    $SOLValue += $Item.ToString() + ","
                    }
                $SOLValue = $SOLValue.Substring(0,($SOLValue.Length -1)).Replace(" ","")
                }

            [string]$ISTValue = $null
            If ($ComputedSRDiffToBeRemoved) {
                #Build a comma sperated list of Ids so we can use it to update the sharepoint list
                Foreach ($Item in $ComputedSRDiffToBeRemoved | sort) {
                    $ISTValue += $Item.ToString() + ","
                    }
                $ISTValue = $ISTValue.Substring(0,($ISTValue.Length -1)).Replace(" ","")
                }

            #Update only if nesccary.
            If ($SOLValue -ne $ISTValue) {
                $SyncLogMessage = "Sync updated the ComputedSRDiffRemoves to " + $SOLValue
                Set-PnPListItem -List $SharePointEmployeesListID -Identity $Employee.ID -Values @{ComputedSRDiffRemoves=IF($SOLValue){$SOLValue}else{0};SyncLog=$SyncLogMessage}
                Write-Host $SyncLogMessage
                } 
                
            }
       
        }
}

#######################################################################################################
#IIS IPFilter Related Functions

Function Assert-IISObjectIPFilters {
     Param( 
    [Parameter(Mandatory=$True)] 
    [string]$IpFiltersListID,
    [Parameter(Mandatory=$True)] 
    [string]$IISListID)
    
    $IPFilters = Get-PnPListItem -List $IpFiltersListID
    $IISObjects = Get-PnPListItem -List $IISListID



    Foreach ($IISObject in $IISObjects) {
        
        [array]$ObjectIPFilters = $IPFilters | Where-Object {$_.FieldValues.ObjectName -eq $IISObject.FieldValues.Title}
        
        IF ($ObjectIPFilters) {
            #Build a comma sperated list of Ids to add to sharepoint.
            [string]$ToBe_Value = $null
                 
            Foreach ($ObjectIPFilter in $ObjectIPFilters) {
                $ToBe_Value += $ObjectIPFilter.Id.ToString() + ","
                }

            $ToBe_Value = $ToBe_Value.Substring(0,($ToBe_Value.Length -1))
            }

        #Get Curent Members (If any)
        If ($IISObject.FieldValues.IpFilters) {
            [array]$CurrentIPFilters = $IISObject.FieldValues.IpFilters

            #Build a comma sperated list of Ids to add to sharepoint.
            [string]$Current_Value = $null
                 
            Foreach ($CurrentIPFilter in $CurrentIPFilters) {
                $Current_Value += $CurrentIPFilter.LookupId.ToString() + ","
                }

            $Current_Value = $Current_Value.Substring(0,($Current_Value.Length -1))
            }
        
        #There are currenlty no filters. So we can just write the new ones.
        IF ((-not($CurrentIPFilters.count -gt 0)) -and ($ObjectIPFilters.Count -gt 0)) {
            $SyncLogMessage = "Get-IISObjectIPFilters updates Ip Filters to: " + $ToBe_Value
            Set-PnPListItem -List $IISListID -Identity $IISObject.Id -Values @{IpFilters=$ToBe_Value;SyncLog=$SyncLogMessage}
            Write-output $SyncLogMessage
            }
    
        #They both have values, lets see if an update is nessecary
        If (($CurrentIPFilters.count -gt 0) -and ($ObjectIPFilters.Count -gt 0)) {
            If (-not($Current_Value -eq $ToBe_Value)) {
                $SyncLogMessage = "Get-IISObjectIPFilters updates Ip Filters to: " + $ToBe_Value
                Set-PnPListItem -List $IISListID -Identity $IISObject.Id -Values @{IpFilters=$ToBe_Value;SyncLog=$SyncLogMessage}
                Write-output $SyncLogMessage
                }
            }

        #It currenlty has Ip filters, but it should not have any, so clear it.
        If (($CurrentIPFilters.count -gt 0) -and (-not($ObjectIPFilters.Count -gt 0))) {
            $SyncLogMessage = "Get-IISObjectIPFilters updates Ip Filters empty."
            Set-PnPListItem -List $IISListID -Identity $IISObject.Id -Values @{IpFilters=0;SyncLog=$SyncLogMessage}
            Write-output $SyncLogMessage
            }

        }



    
    }

Function Assert-IISObjectParent {
    Param([Parameter(Mandatory=$True)] 
        [string]$IISListID)

    $IISObjects = Get-PnPListItem -List $IISListID
    
    Foreach ($IISObject in $IISObjects) {
        
        $ObjectName = $IISObject.Fieldvalues.Title
        $Split = $ObjectName.Split("/")

        $ParentObjectName = $null
        $ParentID = $null
        $CurrentParentId = $null

        IF ($Split.count -gt 3) {
            $ToBeRemoved = $Split[$Split.Count-1]
            [int]$PLenght = $ObjectName.Length - $ToBeRemoved.Length - 1
            $ParentObjectName = $ObjectName.Substring(0,$PLenght)     
            }
          
        if ($ParentObjectName) {
            $Parent = $IISObjects | Where-Object {$_.Fieldvalues.Title -eq $ParentObjectName}
            $ParentID = $Parent.id

            $Object = $IISObjects | Where-Object {$_.Fieldvalues.Title -eq $ObjectName}
            $ObjectID = $Object.id

            }
        
        If ($IISObject.fieldvalues.Parent) {
            $CurrentParentId = ($IISObject.fieldvalues.Parent).LookupId
            }
        
        If ($ParentID -ne $CurrentParentId) {
            
            If ($ParentID -eq $null) {

                Set-PnPListItem -List $IISListID -Identity $IISObject.id -Values @{Parent=0;SyncLog="Assert-ConfigFromParent removed parent."}
                Write-output "Assert-ConfigFromParent removed parent."
                }
            Else {
                
                Set-PnPListItem -List $IISListID -Identity $IISObject.id -Values @{Parent=$ParentID;SyncLog="Assert-ConfigFromParent set parent to $ParentID."}
                Write-output "Assert-ConfigFromParent set parent to $ParentID."
                }

            }

        }
    }

Function Assert-IISObjectConfigFromParent {
     Param( 
    [Parameter(Mandatory=$True)] 
    [string]$IpFiltersListID,
    [Parameter(Mandatory=$True)] 
    [string]$IISListID)

    $IISObjects = Get-PnPListItem -List $IISListID
    $IPFilters = Get-PnPListItem -List $IpFiltersListID
    
    Foreach ($IISObject in $IISObjects) {
        
        $ConfigFromParent = $False
            
        If (($ParentObject.Fieldvalues.EnableReverseDns -eq $IISObject.FieldValues.EnableReverseDns) -and
                 ($ParentObject.Fieldvalues.allowUnlisted -eq $IISObject.FieldValues.allowUnlisted) -and
                 ($ParentObject.Fieldvalues.enableProxyMode -eq $IISObject.FieldValues.enableProxyMode) -and
                 ($ParentObject.Fieldvalues.denyAction -eq $IISObject.FieldValues.denyAction) -and
                 ($ParentObject.Fieldvalues.IsInheritedFromDefaultValue -eq $IISObject.FieldValues.IsInheritedFromDefaultValue)) {
                
                    $ObjectIPFilters = $null
                    IF ($IISObject.Fieldvalues.IpFilters) {
                        $ObjectIPFilters = $IISObject.Fieldvalues.IpFilters
                        }
            
                    $ParentIPFilters = $null
                    If ($IISObject.FieldValues.Parent) {
                        $ParentObject = $IISObjects | Where-Object {$_.id -eq $IISObject.FieldValues.Parent.LookupId}
                        IF ($ParentObject.Fieldvalues.IpFilters) {
                            $ParentIPFilters = $ParentObject.Fieldvalues.IpFilters
                            }
                        }
             
                    If (($ObjectIPFilters -eq $null) -and ($ParentIPFilters -eq $null)) {
                        $ConfigFromParent = $True
                        }
                    ElseIf (($ObjectIPFilters.count -gt 0) -and ($ParentIPFilters.count -gt 0)) {
                
                        $Joins = Find-SyncJoins -SList $ObjectIPFilters -DList $ParentIPFilters -SAnchor "ShortName" -DAnchor "ShortName"

                        If (($Joins.InDestionationNotSource) -or ($Joins.InSourceNotDestionation)) {
                            $ConfigFromParent = $False
                            }
                        Else {
                            $ConfigFromParent = $True
                            }
                        }
                    Else {
                        $ConfigFromParent = $False
                        } 
                

                }
        Else {
            $ConfigFromParent = $False
            }

        $CurrentConfigFromParentValue = $IISObject.FieldValues.ConfigFromParent
        
        If ($CurrentConfigFromParentValue -ne $ConfigFromParent) {
            $SYnclogMessage = "Assert-IISObjectConfigFromParent set ConfigFromParent to $ConfigFromParent."
            Set-PnPListItem -List $IISListID -Identity $IISObject.id -Values @{ConfigFromParent=$ConfigFromParent;SyncLog=$SYnclogMessage}
            Write-output $SYnclogMessage
            
            }

        }
    }

Function Assert-IISIPFilterConfigFromParent {
     Param( 
    [Parameter(Mandatory=$True)] 
    [string]$IpFiltersListID)   
    
    $IPFilters = Get-PnPListItem -List $IpFiltersListID
    
    Foreach ($IPFilter in $IPFilters) {
       
       $ObjectName = $IPFilter.FieldValues.ObjectName
       $ParentObjectName = $ObjectName.Substring(0,$ObjectName.LastIndexOf('/'))
        
       $ParentObject = $IPFilters | Where-Object {$_.FieldValues.ObjectName -eq $ParentObjectName}
       
       if ($ParentObject) {
            
            If ($ParentObject.FieldValues.ShortName -eq $IPFilter.FieldValues.ShortName) {
                If (-not($IPFilter.FieldValues.ExistsOnParent)) {
                    Set-PnPListItem -List $IpFiltersListID -Identity $IPFilter.id -Values @{ExistsOnParent=1;SyncLog="Function Assert-IISIPFilterConfigFromParent set ExistsOnParent to true"}
                    }
                }
            Else {
                If ($IPFilter.FieldValues.ExistsOnParent) {
                    Set-PnPListItem -List $IpFiltersListID -Identity $IPFilter.id -Values @{ExistsOnParent=0;SyncLog="Function Assert-IISIPFilterConfigFromParent set ExistsOnParent to false"}
                    }
                }
            }
        }
    }

#######################################################################################################
#Function Work in Progress

Function Sync-AADProfilePicture2SharePointDocLibary {
    
    #A work in progres..

    $TempFolder = ""
    $AADUsers = Get-AzureADUser

    Foreach ($AADUser in $AADUsers) {
       
       Try {
        $NoCLI = Get-AzureADUserThumbnailPhoto -ObjectId $AADUser.ObjectID -FilePath $TempFolder -FileName ($AADUser.objectID) -ErrorAction SilentlyContinue
        }
       Catch {}

        }
    
    $Photos = Get-ChildItem -Path $TempFolder

    Foreach ($Photo in $Photos) {
        $Photos[0].PSPath
        Add-PnPFile -Path $Photo.FullName -Folder "Pictures"
        
        }


    $Employees = Get-PnPListItem -List $SharePointEmployeesListID

    Foreach ($Employee in $Employees) {
        
        $PictureURL = "" + $Employee.FieldValues.AzureID + ".jpg"
        
        If (Get-PnPFile -Url $PictureURL) {

        Write-Host $PictureURL

        Set-PnPListItem -List $SharePointEmployeesListID -Identity $Employee.Id -Values @{Picture=$PictureURL}
        }
        Else {
            Write-Host "On picture."
            Set-PnPListItem -List $SharePointEmployeesListID -Identity $Employee.Id -Values @{Picture=""}
        
            }

        }

    }