Public/Invoke-iPilotUserSync.ps1
Function Invoke-iPilotUserSync { Param ( [System.String] [Parameter(Mandatory = $false)] $iPilotSyncGroupName = "NuWave iPilot Assigned Phone Numbers", [System.String] [Parameter(Mandatory = $true)] $iPilotUsername, [System.String] [Parameter(Mandatory = $false)] $iPilotDataDirectory = "${env:APPDATA}\NuWave", [System.Int16] [Parameter(Mandatory = $false)] $RetainLogsForDays = 7, [System.String] [Parameter(Mandatory = $false)] $ApiUrl = "https://api.nuwave.com", [System.String] [Parameter(Mandatory = $false)] $Instance ) # Verbose Switch if($PSBoundParameters.containskey("Verbose")) { $PreviousVerbosePreference = $VerbosePreference $VerbosePreference = "continue" } # Debug Switch if($PSBoundParameters.containskey("Debug")) { $PreviousDebugPreference = $DebugPreference $DebugPreference = "continue" } # Create Data Directory New-Item -Path $iPilotDataDirectory -ItemType Directory -Force | Out-Null #region Start Logging $LogFile = "$iPilotDataDirectory\$(Get-Date -Format FileDateTime)_Invoke-iPilotUserSync.log" Start-Transcript -Path $LogFile -Force #endregion Start Logging #region Store/Retreive Credentials $InitializeiPilotSessionSplat = @{} # Add Instance to splat if ($Instance) { $InitializeiPilotSessionSplat += @{ Instance = $Instance } } # Add SaveToFile switch to splat if (Test-Path "$iPilotDataDirectory\iPilot.cred") { $InitializeiPilotSessionSplat += @{ SaveToFile = $true } } Initialize-iPilotSession @InitializeiPilotSessionSplat -ApiUrl $ApiUrl #endregion Store/Retreive Credentials #region Authenticate to Azure & iPilot # Authenticate to iPilot Get-iPilotTeamsDomain -Credential $global:IP_iPilotCredential #region Get Azure AD OAuth Application Token for Graph API # Get Token Get-iPilotDirectorySyncOAuthToken # Set Header $AzureADGraphHeaders = @{ Authorization = "Bearer $iPilotDirectorySyncOAuthToken" } Write-Verbose "Header: $($AzureADGraphHeaders | Out-String)" #endregion Get Azure AD OAuth Application Token for Graph API #endregion Authenticate to Azure & iPilot #region Get user and group changes from Azure AD #region Get Members of iPilot Sync group # Get Group's Object ID $iPilotSyncGroupObjectIDUri = 'https://graph.microsoft.com/v1.0/groups?$filter=startswith(displayName,''' + [System.Web.HttpUtility]::UrlPathEncode($iPilotSyncGroupName) + ''')&$select=id' $iPilotSyncGroupObjectID = Invoke-RestMethod -Uri $iPilotSyncGroupObjectIDUri -Method Get -Headers $AzureADGraphHeaders | Select-Object -ExpandProperty value | Select-Object -ExpandProperty id #region Get Sync Group Members $iPilotSyncGroupMembersUri = "https://graph.microsoft.com/v1.0/groups/$iPilotSyncGroupObjectId/members?`$select=id,userPrincipalName,displayName,givenName,surname,businessPhones,mobilePhone,accountEnabled" Try { $iPilotSyncGroupMembersResponse = Invoke-RestMethod -Uri $iPilotSyncGroupMembersUri -Method Get -Headers $AzureADGraphHeaders Write-Debug "Invoke-RestMethod -Method Get -Uri $iPilotSyncGroupMembersUri -Headers @(Authorization = ""Bearer $iPilotDirectorySyncOAuthToken"") -ContentType ""application/json""`n" } Catch { Write-Error "Failed to get members of $iPilotSyncGroupName. Exiting" #Exit 1 } $iPilotSyncGroupMembers = $iPilotSyncGroupMembersResponse | Select-Object -ExpandProperty value # Follow all nextLinks $i = 2 Do { # Get response from nextLink if ($iPilotSyncGroupMembersResponse.'@odata.nextLink') { Try { $iPilotSyncGroupMembersResponse = Invoke-RestMethod -Method Get -Uri $iPilotSyncGroupMembersResponse.'@odata.nextLink' -Headers $AzureADGraphHeaders -ContentType "application/json" Write-Debug "Invoke-RestMethod -Method Get -Uri $($iPilotSyncGroupMembersResponse.'@odata.nextLink') -Headers @(Authorization = ""Bearer $iPilotDirectorySyncOAuthToken"") -ContentType ""application/json""`n" } Catch { Write-Error "Failed to pull initial list of all Azure AD Users. Exiting" #Exit 1 } $iPilotSyncGroupMembers += $iPilotSyncGroupMembersResponse | Select-Object -ExpandProperty value Write-Verbose "`nGetDirectoryObjectsInitialRequest #$($i): $($iPilotSyncGroupMembersResponse.value.Count) records returned`n" $i++ } } While ($iPilotSyncGroupMembersResponse.'@odata.nextLink' -and $iPilotSyncGroupMembersResponse.value.Count -ge 1) Write-Verbose "iPilotSyncGroupMembers:`n$($iPilotSyncGroupMembers | Format-Table | Out-String)`n" #endregion Get Sync Group Members #region Get Sync Group Member Telephone Numbers Foreach ($iPilotSyncGroupMember in $iPilotSyncGroupMembers) { $iPilotSyncGroupMemberPropertiesUri = "https://graph.microsoft.com/v1.0/users/$($iPilotSyncGroupMember.id)?`$select=id,userPrincipalName,ipPhone,telephoneNumber,businessPhones,accountEnabled" Write-Verbose "Appending ipPhone,telephoneNumber,businessPhones attributes to $($iPilotSyncGroupMember.userPrincipalName) object (id=$($iPilotSyncGroupMember.id))`nURI: $iPilotSyncGroupMemberPropertiesUri" Try { $iPilotSyncGroupMemberProperties = Invoke-RestMethod -Uri $iPilotSyncGroupMemberPropertiesUri -Method Get -Headers $AzureADGraphHeaders Write-Debug "Invoke-RestMethod -Method Get -Uri $iPilotSyncGroupMemberPropertiesUri -Headers @(Authorization = ""Bearer $iPilotDirectorySyncOAuthToken"") -ContentType ""application/json""`n" } Catch { Write-Warning "Failed to retrieve ipPhone,telephoneNumber,businessPhones attributes for $($iPilotSyncGroupMember.userPrincipalName)" } # Append Business Phone to object if($iPilotSyncGroupMemberProperties.businessPhones) { Write-Verbose "$($iPilotSyncGroupMemberProperties.userPrincipalName) - businessPhones = $($iPilotSyncGroupMemberProperties.businessPhones -join ",")" $iPilotSyncGroupMember | Add-Member -MemberType NoteProperty -Name businessPhone -Value $iPilotSyncGroupMemberProperties.businessPhones[0] -Force # Drop all but last 10 digits if number exceeds 10 digits if ($iPilotSyncGroupMember.businessPhone.length -gt 10) { Write-Verbose "$($iPilotSyncGroupMember.userPrincipalName) - businessPhone length exceeds 10 digits. Actual value: $($iPilotSyncGroupMember.businessPhone)" $iPilotSyncGroupMember | Add-Member -MemberType NoteProperty -Name businessPhone -Value $iPilotSyncGroupMember.businessPhone.Substring($iPilotSyncGroupMember.businessPhone.Length - 10) -Force Write-Verbose "$($iPilotSyncGroupMember.userPrincipalName) - businessPhone new value: $($iPilotSyncGroupMember.businessPhone)" } } else { # Set business phone to $null if no businessPhones found $iPilotSyncGroupMember | Add-Member -MemberType NoteProperty -Name businessPhone -Value $null Write-Debug "$($iPilotSyncGroupMemberProperties.userPrincipalName) - businessPhone is empty" } # Append ipPhone to object if ((Get-Member -InputObject $iPilotSyncGroupMemberProperties).Name -contains "ipPhone") { Write-Verbose "$($iPilotSyncGroupMemberProperties.userPrincipalName) - ipPhone = $($iPilotSyncGroupMemberProperties.ipPhone)" $iPilotSyncGroupMember | Add-Member -MemberType NoteProperty -Name ipPhone -Value $iPilotSyncGroupMemberProperties.ipPhone } else { Write-Debug "$($iPilotSyncGroupMemberProperties.userPrincipalName) - ipPhone is empty" } # Append telephoneNumber to object if ((Get-Member -InputObject $iPilotSyncGroupMemberProperties).Name -contains "telephoneNumber") { Write-Verbose "$($iPilotSyncGroupMemberProperties.userPrincipalName) - telephoneNumber = $($iPilotSyncGroupMemberProperties.telephoneNumber)" $iPilotSyncGroupMember | Add-Member -MemberType NoteProperty -Name telephoneNumber -Value $iPilotSyncGroupMemberProperties.telephoneNumber } else { Write-Debug "$($iPilotSyncGroupMemberProperties.userPrincipalName) - telephoneNumber is empty" } } #endregion Get Sync Group Member Telephone Numbers #endregion Get Members of iPilot Sync group #region Perform Initial Sync/Delta Sync & Get Changed Users $AzureADUsers = @() # Initial lookup of directory if (!(Test-Path -Path "$iPilotDataDirectory\UserDeltaLink_$($iPilotDomain).txt")) { Write-Output "Performing initial pull of all users from Azure AD`n" # Get initial lookup of id, displayName, userPrincipalName, givenName, surname, & accountEnabled $GetDirectoryObjectsInitialUri = 'https://graph.microsoft.com/v1.0/users/delta?$select=id,userPrincipalName,givenName,surname,accountEnabled,businessPhones' Write-Verbose "GetDirectoryObjectsInitialUri: $GetDirectoryObjectsInitialUri`n" # Get initial user request response Try { $GetDirectoryObjectsInitialRequest = Invoke-RestMethod -Method Get -Uri $GetDirectoryObjectsInitialUri -Headers $AzureADGraphHeaders -ContentType "application/json" Write-Debug "Invoke-RestMethod -Method Get -Uri $GetDirectoryObjectsInitialUri -Headers @(Authorization = ""Bearer $iPilotDirectorySyncOAuthToken"") -ContentType ""application/json""`n" } Catch { Write-Error "Failed to pull initial list of all Azure AD Users. Exiting. Error Message: $($_.Exception.Message)" #Exit 1 } # Get initial values $AzureADUsers += $GetDirectoryObjectsInitialRequest.value # Follow all nextLinks $i = 2 Do { # Get response from nextLink if ($GetDirectoryObjectsInitialRequest.'@odata.nextLink') { Try { $GetDirectoryObjectsInitialRequest = Invoke-RestMethod -Method Get -Uri $GetDirectoryObjectsInitialRequest.'@odata.nextLink' -Headers $AzureADGraphHeaders -ContentType "application/json" Write-Debug "Invoke-RestMethod -Method Get -Uri $($GetDirectoryObjectsInitialRequest.'@odata.nextLink') -Headers @(Authorization = ""Bearer $iPilotDirectorySyncOAuthToken"") -ContentType ""application/json""`n" } Catch { Write-Error "Failed to pull initial list of all Azure AD Users. Exiting" #Exit 1 } $AzureADUsers += $GetDirectoryObjectsInitialRequest.value Write-Verbose "`nGetDirectoryObjectsInitialRequest #$($i): $($GetDirectoryObjectsInitialRequest.value.Count) records returned`n" $i++ } } While ($GetDirectoryObjectsInitialRequest.'@odata.nextLink' -and $GetDirectoryObjectsInitialRequest.value.Count -ge 1) Write-Verbose "All Azure AD users:`n$($AzureADUsers | Format-Table | Out-String)`n" # Output next link to deltaLink file for baseline if ($GetDirectoryObjectsInitialRequest.'@odata.deltaLink') { Write-Output "Output next link to $("$iPilotDataDirectory\UserDeltaLink_$($iPilotDomain).txt") for next sync`n" $GetDirectoryObjectsInitialRequest.'@odata.deltaLink' | Out-File "$iPilotDataDirectory\UserDeltaLink_$($iPilotDomain).txt" -Force } else { Write-Error "No @odata.deltaLink returned from query to $GetDirectoryObjectsInitialUri" } } else { Write-Output "Performing delta pull of changed users from Azure AD`n" # Get DeltaLink URI from file $GetDirectoryObjectsDeltaUri = Get-Content -Path "$iPilotDataDirectory\UserDeltaLink_$($iPilotDomain).txt" # Get Delta Try { $GetDirectoryObjectsDeltaRequest = Invoke-RestMethod -Method Get -Uri $GetDirectoryObjectsDeltaUri -Headers $AzureADGraphHeaders -ContentType "application/json" Write-Debug "Invoke-RestMethod -Method Get -Uri $GetDirectoryObjectsDeltaUri -Headers @(Authorization = ""Bearer $iPilotDirectorySyncOAuthToken"") -ContentType ""application/json""`n" } Catch { Write-Error "Failed to pull delta list of all Azure AD Users. Exiting" #Exit 1 } # Get Changed Users since last initial or delta request $AzureADUsers += $GetDirectoryObjectsDeltaRequest.value # Follow all nextLinks $i = 2 Do { # Get response from nextLink if ($GetDirectoryObjectsDeltaRequest.'@odata.nextLink') { Try { $GetDirectoryObjectsDeltaRequest = Invoke-RestMethod -Method Get -Uri $GetDirectoryObjectsDeltaRequest.'@odata.nextLink' -Headers $AzureADGraphHeaders -ContentType "application/json" Write-Debug "Invoke-RestMethod -Method Get -Uri $($GetDirectoryObjectsDeltaRequest.'@odata.nextLink') -Headers @(Authorization = ""Bearer $iPilotDirectorySyncOAuthToken"") -ContentType ""application/json""`n" } Catch { Write-Error "Failed to pull delta list of all Azure AD Users. Exiting" #Exit 1 } $AzureADUsers += $GetDirectoryObjectsDeltaRequest.value Write-Verbose "GetDirectoryObjectsInitialRequest #$($i): $($GetDirectoryObjectsDeltaRequest.value.Count) records returned`n" $i++ } } While ($GetDirectoryObjectsDeltaRequest.'@odata.nextLink') # Output AzureADUsers if ($AzureADUsers) { Write-Output "Changed Azure AD users since last sync:`n$($AzureADUsers | Format-Table | Out-String)`n" } # Output next link to deltaLink file for next delta sync if ($GetDirectoryObjectsDeltaRequest.'@odata.deltaLink') { Write-Output "Output next link to $("$iPilotDataDirectory\UserDeltaLink_$($iPilotDomain).txt") for next sync`n" $GetDirectoryObjectsDeltaRequest.'@odata.deltaLink' | Out-File "$iPilotDataDirectory\UserDeltaLink_$($iPilotDomain).txt" -Force } else { Write-Error "No @odata.deltaLink returned from query to $GetDirectoryObjectsDeltaUri" } } #endregion Perform Initial Sync/Delta Sync & Get Changed Users #region Perform Initial Sync/Delta Lookup of iPilot Sync Group Members # Initial lookup of directory if (!(Test-Path -Path "$iPilotDataDirectory\GroupDeltaLink_$($iPilotDomain).txt")) { Write-Output "Performing initial query of $iPilotSyncGroupName members from Azure AD" # Get initial lookup of group members $GetDirectoryGroupInitialUri = "https://graph.microsoft.com/v1.0/groups/delta?`$filter=id eq `'" + $iPilotSyncGroupObjectID + "`'" Write-Verbose "GetDirectoryGroupsInitialUri: $GetDirectoryGroupsInitialUri" # Get initial group request response Try { $GetDirectoryGroupInitialRequest = Invoke-RestMethod -Method Get -Uri $GetDirectoryGroupInitialUri -Headers $AzureADGraphHeaders -ContentType "application/json" Write-Debug "Invoke-RestMethod -Method Get -Uri $GetDirectoryGroupInitialUri -Headers @(Authorization = ""Bearer $iPilotDirectorySyncOAuthToken"") -ContentType ""application/json""" } Catch { Write-Error "Failed to perform Initial Lookup of $iPilotSyncGroupName Members. Exiting" #Exit 1 } # Get initial values $iPilotSyncGroupMemberIds = $GetDirectoryGroupInitialRequest.Value.'members@delta'.id # Follow all nextLinks $i = 2 Do { # Get response from nextLink if ($GetDirectoryGroupInitialRequest.'@odata.nextLink') { Try { $GetDirectoryGroupInitialRequest = Invoke-RestMethod -Method Get -Uri $GetDirectoryGroupInitialRequest.'@odata.nextLink' -Headers $AzureADGraphHeaders -ContentType "application/json" Write-Debug "Invoke-RestMethod -Method Get -Uri $($GetDirectoryGroupInitialRequest.'@odata.nextLink') -Headers @(Authorization = ""Bearer $iPilotDirectorySyncOAuthToken"") -ContentType ""application/json""" } Catch { Write-Error "Failed to perform initial lookup of $iPilotSyncGroupName Members. Exiting" #Exit 1 } $iPilotSyncGroupMemberIds += $GetDirectoryGroupInitialRequest.Value.'members@delta'.id Write-Verbose "GetDirectoryGroupInitialRequest #$($i): $($GetDirectoryGroupInitialRequest.Value.'members@delta'.id.Count) records returned" $i++ } } While ($GetDirectoryGroupInitialRequest.'@odata.nextLink' -and $GetDirectoryGroupInitialRequest.Value.'members@delta'.id.Count -ge 1) Write-Verbose "iPilotSyncGroupMemberIds:`n$($iPilotSyncGroupMemberIds | Format-Table | Out-String)" # Output deltaLink to file for next sync if ($GetDirectoryGroupInitialRequest.'@odata.deltaLink') { Write-Output "Output next link to $("$iPilotDataDirectory\GroupDeltaLink_$($iPilotDomain).txt") for next sync" $GetDirectoryGroupInitialRequest.'@odata.deltaLink' | Out-File "$iPilotDataDirectory\GroupDeltaLink_$($iPilotDomain).txt" -Force } else { Write-Error "No @odata.deltaLink returned from query to $GetDirectoryGroupInitialUri" } } else { Write-Output "Performing delta query of group members of $iPilotSyncGroupName from Azure AD" # Get DeltaLink URI from file $GetDirectoryGroupDeltaUri = Get-Content -Path "$iPilotDataDirectory\GroupDeltaLink_$($iPilotDomain).txt" # Get Delta Try { $GetDirectoryGroupDeltaRequest = Invoke-RestMethod -Method Get -Uri $GetDirectoryGroupDeltaUri -Headers $AzureADGraphHeaders -ContentType "application/json" Write-Debug "Invoke-RestMethod -Method Get -Uri $GetDirectoryGroupDeltaUri -Headers @(Authorization = ""Bearer $iPilotDirectorySyncOAuthToken"") -ContentType ""application/json""" } Catch { Write-Error "Failed to perform delta lookup of $iPilotSyncGroupName Members. Exiting" #Exit 1 } # Get delta values $iPilotSyncGroupMemberIds = $GetDirectoryGroupDeltaRequest.Value.'members@delta'.id # Follow all nextLinks $i = 2 Do { # Get response from nextLink if ($GetDirectoryGroupDeltaRequest.'@odata.nextLink') { Try { $GetDirectoryGroupDeltaRequest = Invoke-RestMethod -Method Get -Uri $GetDirectoryGroupDeltaRequest.'@odata.nextLink' -Headers $AzureADGraphHeaders -ContentType "application/json" Write-Debug "Invoke-RestMethod -Method Get -Uri $($GetDirectoryGroupDeltaRequest.'@odata.nextLink') -Headers @(Authorization = ""Bearer $iPilotDirectorySyncOAuthToken"") -ContentType ""application/json""" } Catch { Write-Error "Failed to perform delta lookup of $iPilotSyncGroupName Members. Exiting" #Exit 1 } $iPilotSyncGroupMemberIds += $GetDirectoryGroupDeltaRequest.Value.'members@delta'.id Write-Verbose "GetDirectoryGroupDeltaRequest #$($i): $($GetDirectoryGroupDeltaRequest.Value.'members@delta'.id.Count) records returned" $i++ } } While ($GetDirectoryGroupDeltaRequest.'@odata.nextLink') # Output iPilotSyncGroupMemberIds if ($iPilotSyncGroupMemberIds) { Write-Verbose "iPilotSyncGroupMemberIds:`n$($iPilotSyncGroupMemberIds | Format-Table | Out-String)" } # Get Users Added To iPilot Sync Group $UsersAddedToiPilotSyncGroup = $iPilotSyncGroupMemberIds | ForEach-Object { $iPilotSyncGroupMemberId = $_ $iPilotSyncGroupMember = $iPilotSyncGroupMembers | Where-Object {$_.id -eq $iPilotSyncGroupMemberId} # Set UsersAddedToiPilotSyncGroup to true $iPilotSyncGroupMember | Add-Member -MemberType NoteProperty -Name AddedToiPilotSyncGroup -Value $true -Force return $iPilotSyncGroupMember } # Output users added to iPilot Sync Group if ($UsersAddedToiPilotSyncGroup) { Write-Output "Users Added To iPilot Sync Group:`n$($UsersAddedToiPilotSyncGroup | Select-Object givenName,surname,id,userPrincipalName,businessPhone,AddedToiPilotSyncGroup,accountEnabled,businessPhones | Format-Table -AutoSize | Out-String)" } # Append users to AzureAdUsers $AzureADUsers += $UsersAddedToiPilotSyncGroup #region Get Users Removed To iPilot Sync Group $UsersRemovedFromiPilotSyncGroup = @() if ($GetDirectoryGroupDeltaRequest.value.'members@delta') { $GetDirectoryGroupDeltaRequest.value.'members@delta' | ForEach-Object { $UserRemovedFromiPilotSyncGroup = $_ Write-Output "RemovediPilotSyncGroupMemberId: $($_.id)" if ($UserRemovedFromiPilotSyncGroup.'@removed'.reason -eq "deleted") { $RemovediPilotSyncGroupMemberId = $_.id #region Get user's details $RemovediPilotSyncGroupMemberPropertiesUri = "https://graph.microsoft.com/v1.0/users/$($RemovediPilotSyncGroupMemberId)?`$select=id,userPrincipalName,givenName,surname,ipPhone,telephoneNumber,businessPhones,accountEnabled" Try { $RemovediPilotSyncGroupMemberProperties = Invoke-RestMethod -Uri $RemovediPilotSyncGroupMemberPropertiesUri -Method Get -Headers $AzureADGraphHeaders Write-Debug "Invoke-RestMethod -Method Get -Uri $RemovediPilotSyncGroupMemberPropertiesUri -Headers @(Authorization = ""Bearer $iPilotDirectorySyncOAuthToken"") -ContentType ""application/json""`n" } Catch { Write-Warning "Failed to retrieve ipPhone,telephoneNumber,businessPhones attributes for Azure AD id: $RemovediPilotSyncGroupMemberId" } #endregion Get user's details $RemovediPilotSyncGroupMember = $RemovediPilotSyncGroupMemberProperties | Select-Object id,userPrincipalName,givenName,surname,businessPhones Write-Output "Adding $($RemovediPilotSyncGroupMember.userPrincipalName) to UsersRemovedToiPilotSyncGroup" # Set RemovedToiPilotSyncGroup to true $RemovediPilotSyncGroupMember | Add-Member -MemberType NoteProperty -Name RemovedFromiPilotSyncGroup -Value $true -Force $UsersRemovedFromiPilotSyncGroup += $RemovediPilotSyncGroupMember } } # Append users to AzureAdUsers $AzureADUsers += $UsersRemovedFromiPilotSyncGroup } #endregion Get Users Removed To iPilot Sync Group # Output deltaLink to file for next sync if ($GetDirectoryGroupDeltaRequest.'@odata.deltaLink') { Write-Output "Output next link to $("$iPilotDataDirectory\GroupDeltaLink_$($iPilotDomain).txt") for next sync" $GetDirectoryGroupDeltaRequest.'@odata.deltaLink' | Out-File "$iPilotDataDirectory\GroupDeltaLink_$($iPilotDomain).txt" -Force } else { Write-Error "No @odata.deltaLink returned from query to $GetDirectoryGroupDeltaUri" } } #endregion Perform Initial Sync/Delta Lookup of iPilot Sync Group Members #region Get list of changed users Write-Output "Getting changed users from last sync that are in $iPilotSyncGroupName" $ChangedUsers = @() $ChangedUsers += $AzureADUsers | Where-Object { $ItemToCheck = $_ $iPilotSyncGroupMembers | Where-Object { $ItemToCheck -match $_.id } } # Add UsersRemovedFromiPilotSyncGroup to ChangedUsers $ChangedUsers += $UsersRemovedFromiPilotSyncGroup $ChangedUsers = $ChangedUsers | Where-Object {$_} # Filters out any empty items if ($ChangedUsers) { Write-Output "Changed Users:`n$($ChangedUsers | Format-Table -AutoSize | Out-String)" } #endregion Get list of changed users #region Get objectGuid to iPilot ID mapping # Local JSON file path $JsonPath = "$iPilotDataDirectory\iPilotUserMapping_$($iPilotDomain).json" # Import existing JSON if exists, else create JSON if (Test-Path $JsonPath) { Write-Verbose "Retrieving saved objectGUID and iPilot ID mapping located at $JsonPath" $SavediPilotUserMapping = Get-Content $JsonPath | ConvertFrom-Json Write-Verbose "SavediPilotUserMapping:`n$($SavediPilotUserMapping | Format-Table -AutoSize | Out-String)" } # Get all users from iPilot Try { Write-Output "Getting all assigned iPilot numbers " Get-iPilotTeamsDomain -Credential $iPilotCredential -ErrorAction Stop $AlliPilotUsers = Get-iPilotNumber -UserPrincipalNameAssigned | Where-Object {$_.UserPrincipalName} } Catch { Try { Write-Output "Refreshing iPilot Token" Get-iPilotTeamsDomain -Credential $iPilotCredential -RefreshToken -Verbose -ErrorAction Stop $AlliPilotUsers = Get-iPilotNumber -UserPrincipalNameAssigned | Where-Object {$_.UserPrincipalName} } Catch { Write-Error "Failed to retreive all iPilot users. Exiting with failure.`nError: $($_.Exception.Message)" Exit 1 } } # Loop through each user found in Azure AD and map UserID, iPilotFirstName, and iPilotLastName Foreach ($User in $ChangedUsers) { # Add objectGUID to ChangedUsers $User | Add-Member -MemberType NoteProperty -Name objectGUID -Value $User.id -Force # Add UserID to ChangedUsers $User | Add-Member -MemberType NoteProperty -Name UserID -Value ( $AlliPilotUsers | Where-Object {$_.UserPrincipalName -eq $User.UserPrincipalName} | Select-Object -ExpandProperty UserID) -Force # Add iPilotFirstName to ChangedUsers $User | Add-Member -MemberType NoteProperty -Name iPilotFirstName -Value ( $AlliPilotUsers | Where-Object {$_.UserPrincipalName -eq $User.UserPrincipalName} | Select-Object -ExpandProperty FirstName) -Force # Add iPilotLastName to ChangedUsers $User | Add-Member -MemberType NoteProperty -Name iPilotLastName -Value ( $AlliPilotUsers | Where-Object {$_.UserPrincipalName -eq $User.UserPrincipalName} | Select-Object -ExpandProperty LastName) -Force } # Export JSON with UserPrincipalName, UserID (iPilot) and objectGUID and check if saved JSON has a UPN change if ($SavediPilotUserMapping) { $ChangedUserPrincipalNames = @() Foreach ($SavediPilotUser in $SavediPilotUserMapping) { # Check if user is in saved JSON $CurrentiPilotUser = $ChangedUsers | Where-Object {$_.objectGUID -eq $SavediPilotUser.objectGUID} if ($CurrentiPilotUser) { # Check if UPN has changed if ($CurrentiPilotUser.userPrincipalName -ne $SavediPilotUser.userPrincipalName) { Write-Output "$($SavediPilotUser.userPrincipalName) has changed to $($CurrentiPilotUser.userPrincipalName)" $CurrentiPilotUser | Add-Member -MemberType NoteProperty -Name PreviousUPN -Value $SavediPilotUser.userPrincipalName -Force $ChangedUserPrincipalNames += $CurrentiPilotUser } } } } # Update saved iPilot User Mapping $SavediPilotUserMapping | ForEach-Object { # Update saved iPilot User Mapping $ChangedUser = $ChangedUsers | Where-Object {$_.objectGUID -match $SavediPilotUserMapping.objectGUID} if ($ChangedUser) { $SavediPilotUserMapping | Where-Object {$_.objectGUID -match $SavediPilotUserMapping.objectGUID} Write-Verbose "Updating saved iPilot user mapping for $($ChangedUser.userPrincipalName). objectGUID: $($ChangedUser.objectGUID)" } } # Check for new users not in SavediPilotUserMapping if ($SavediPilotUserMapping) { if($ChangedUsers) { $Comparision = Compare-Object -ReferenceObject $SavediPilotUserMapping.objectGUID -DifferenceObject $ChangedUsers.objectGUID $NewUsers = $Comparision | ForEach-Object { if ($_.SideIndicator -eq "=>") { $objectGUID = $_.InputObject $ChangedUsers | Where-Object {$_.objectGUID -eq $objectGUID} | Select-Object userPrincipalName,UserID,objectGUID } } $iPilotUserMapping = $SavediPilotUserMapping } else { $iPilotUserMapping = $SavediPilotUserMapping + $NewUsers } } else { # Store initial lookup of UPNs, UserIDs and objectGUIDs Write-Verbose "Store initial lookup of UPNs, UserIDs and objectGUIDs" $iPilotUserMapping = $ChangedUsers | Select-Object userPrincipalName,UserID,objectGUID } Write-Verbose "iPilotUserMapping:`n$($iPilotUserMapping | Format-Table -AutoSize | Out-String)" # Save iPilotUserMapping to JSON $iPilotUserMapping | ConvertTo-Json | Out-File $JsonPath -Force -Verbose # Exit with success after initial sync if (!$SavediPilotUserMapping) { Write-Output "Exiting with success after initial sync" Stop-Transcript -Verbose Exit 0 } #endregion Get objectGuid to iPilot ID mapping #region Map objectGUID to UPN & UserID $iPilotUsersToUpdate = @() Foreach ($iPilotUser in $ChangedUsers) { # Azure AD Fist Name $iPilotUser | Add-Member -MemberType NoteProperty -Name AzureADFirstName -Value ( $AzureADUsers | Where-Object {$_.id -eq $iPilotUser.objectGUID} | Select-Object -ExpandProperty givenName) -Force # Azure AD Last Name $iPilotUser | Add-Member -MemberType NoteProperty -Name AzureADLastName -Value ( $AzureADUsers | Where-Object {$_.id -eq $iPilotUser.objectGUID} | Select-Object -ExpandProperty surname) -Force # Azure AD UserPrincipalName $iPilotUser | Add-Member -MemberType NoteProperty -Name AzureADUserPrincipalName -Value ( $AzureADUsers | Where-Object {$_.id -eq $iPilotUser.objectGUID} | Select-Object -ExpandProperty userPrincipalName) -Force # iPilot BusinessPhone if ($iPilotUser.businessPhones) { $iPilotUser | Add-Member -MemberType NoteProperty -Name BusinessPhone -Value ( $iPilotUser.businessPhones.Substring($iPilotUser.businessPhones[0].Length - 10)) -Force } # iPilot First Name $iPilotUser | Add-Member -MemberType NoteProperty -Name iPilotFirstName -Value ( $AlliPilotUsers | Where-Object {$_.UserID -eq $iPilotUser.UserID} | Select-Object -ExpandProperty FirstName) -Force # iPilot Last Name $iPilotUser | Add-Member -MemberType NoteProperty -Name iPilotLastName -Value ( $AlliPilotUsers | Where-Object {$_.UserID -eq $iPilotUser.UserID} | Select-Object -ExpandProperty LastName) -Force # iPilot UserPrincipalName $iPilotUser | Add-Member -MemberType NoteProperty -Name iPilotUserPrincipalName -Value ( $AlliPilotUsers | Where-Object {$_.UserID -eq $iPilotUser.UserID} | Select-Object -ExpandProperty UserPrincipalName) -Force # If iPilot First Name does not match Azure AD First Name, put user into array to be updated in iPilot if ($iPilotUser.AzureADFirstName -ne $iPilotUser.iPilotFirstName) { $iPilotUsersToUpdate += $iPilotUser Write-Output "First Name Changed - Adding $($iPilotUser.AzureADUserPrincipalName) to iPilotUsersToUpdate:`n$($iPilotUser | Format-List | Out-String)" } # If iPilot Last Name does not match Azure AD Last Name, put user into array to be updated in iPilot if ($iPilotUser.AzureADLastName -ne $iPilotUser.iPilotLastName) { if($iPilotUsersToUpdate -notcontains $iPilotUser) { $iPilotUsersToUpdate += $iPilotUser } Write-Output "Last Name Changed - Adding $($iPilotUser.AzureADUserPrincipalName) to iPilotUsersToUpdate:`n$($iPilotUser | Format-List | Out-String)" } # If iPilot UPN does not match Azure AD UPN, put user into array to be updated in iPilot if ($iPilotUser.AzureADUserPrincipalName -ne $iPilotUser.iPilotUserPrincipalName) { if($iPilotUsersToUpdate -notcontains $iPilotUser) { $iPilotUsersToUpdate += $iPilotUser } Write-Output "UPN Changed - Adding $($iPilotUser.AzureADUserPrincipalName) to iPilotUsersToUpdate:`n$($iPilotUser | Format-List | Out-String)" } # If iPilot number does not match Azure AD businessPhones, put user into array to be updated in iPilot if ($iPilotUser.iPilotUserPrincipalName) { $iPilotUser | Add-Member -Name ExistingiPilotTelephoneNumber -MemberType NoteProperty -Value ( $AlliPilotUsers | Where-Object {$_.UserPrincipalName -eq $iPilotUser.iPilotUserPrincipalName} | Select-Object -ExpandProperty TelephoneNumber ) -Force if ($iPilotUser.BusinessPhone -ne $iPilotUser.ExistingiPilotTelephoneNumber) { if($iPilotUsersToUpdate -notcontains $iPilotUser) { $iPilotUsersToUpdate += $iPilotUser } Write-Output "BusinessPhone Changed - Adding $($iPilotUser.AzureADUserPrincipalName) to iPilotUsersToUpdate:`n$($iPilotUser | Format-List | Out-String)" } } # Added to group if ($iPilotUser.AddedToiPilotSyncGroup) { if($iPilotUsersToUpdate -notcontains $iPilotUser) { $iPilotUsersToUpdate += $iPilotUser } Write-Output "Added to $($iPilotSyncGroupName) - Adding $($iPilotUser.AzureADUserPrincipalName) to iPilotUsersToUpdate:`n$($iPilotUser | Format-List | Out-String)" } } #endregion Map objectGUID to UPN & UserID #endregion Get user and group changes from Azure AD #region Perform sync #region Provision users from previous sync # Create array to store user accounts that will be written to disk for retreival on next sync $UsersToProvisionOnNextSync = @() # Retreive users to provision on this sync if (Test-Path "$iPilotDataDirectory\UsersToProvisionFromLastSync_$($iPilotDomain).xml") { Write-Output "Retreiving users to provision on this sync from $iPilotDataDirectory\UsersToProvisionFromLastSync_$($iPilotDomain).xml" $UsersToProvisionFromLastSync = Import-Clixml "$iPilotDataDirectory\UsersToProvisionFromLastSync_$($iPilotDomain).xml" # Provision users if ($UsersToProvisionFromLastSync) { Foreach ($iPilotUser in $UsersToProvisionFromLastSync) { Write-Output "Provisioning $($iPilotUser.AzureADUserPrincipalName) from last sync with $($iPilotUser.BusinessPhone)" $NewiPilotUserSplat = @{ UserPrincipalName = $iPilotUser.AzureADUserPrincipalName FirstName = $iPilotUser.AzureADFirstName LastName = $iPilotUser.AzureADLastName TelephoneNumber = $iPilotUser.BusinessPhone } Try { New-iPilotTeamsUserAssignment @NewiPilotUserSplat } Catch { Write-Error "Failed to provision $($iPilotUser.AzureADUserPrincipalName) with $($iPilotUser.BusinessPhone). Will retry next sync.`nError:$($_)" $UsersToProvisionOnNextSync += $iPilotUser } } } else { Write-Output "No users to provision found in $iPilotDataDirectory\UsersToProvisionFromLastSync_$($iPilotDomain).xml" } } #endregion Provision users from previous sync #region Provision new users/update users with name or number changes # Output to log if no users to update if (!$iPilotUsersToUpdate) { Write-Output "No users to update" } Foreach ($global:IP_iPilotUser in $iPilotUsersToUpdate) { #region Added to Group # if iPilotUserPrincipalName is not populated, user needs to be provisioned if (!$iPilotUser.iPilotUserPrincipalName) { Write-Verbose "$($iPilotUser.AzureADUserPrincipalName)'s iPilotUserPrincipalName is blank. Will provision account." $ProvisionUser = $true } else { $ProvisionUser = $false } if ($iPilotUser.AddedToiPilotSyncGroup -or $ProvisionUser) { #region Provision User if (!$iPilotUser.iPilotUserPrincipalName -and !$iPilotUser.UserID -and $iPilotUser.accountEnabled -eq $true) { Write-Output "Provisioning new user:`n UserPrincipalName: $($iPilotUser.AzureADUserPrincipalName)`n FirstName: $($iPilotUser.AzureADFirstName)`n LastName: $($iPilotUser.AzureADLastName)" Write-Debug "iPilotUserPrincipalName and iPilot UserID are empty" # Check if user has existing businessPhone attribute #$iPilotUser.BusinessPhone = "8132124481" if ($iPilotUser.BusinessPhone) { Write-Debug "iPilotUser.BusinessPhone = $($iPilotUser.BusinessPhone)" # Check if BusinessPhone is an available number in iPilot $TelephoneNumber = Get-iPilotNumber -FilterByTelephoneNumber $iPilotUser.BusinessPhone -iPilotDomain $iPilotDomain -Credential $iPilotCredential -Verbose Write-Verbose "TelephoneNumber:`n$($TelephoneNumber | Format-List | Out-String)" # BusinessPhone (AzureAD) matches number in iPilot (TelephoneNumber) if ($TelephoneNumber) { # If phone number was changed and number exists unassigned in iPilot, auto deprovision the old number (if one), and auto provision the new number if (!$TelephoneNumber.ProvisionUser.upn) { # If TelephoneNumber is not assigned to another user Write-Debug "TelephoneNumber.ProvisionUser.upn is blank" Try { $CurrentiPilotTelephoneNumber = Get-iPilotTeamsUser -FilterByUserPrincipalName $iPilotUser.AzureADUserPrincipalName | Select-Object -ExpandProperty TelephoneNumber } Catch { Write-Verbose "No existing iPilot TelephoneNumber assigned." Write-Debug "`$CurrentiPilotTelephoneNumber = Get-iPilotTeamsUser -FilterByUserPrincipalName $($iPilotUser.AzureADUserPrincipalName) | Select-Object -ExpandProperty TelephoneNumber" } Write-Verbose "CurrentiPilotTelephoneNumber: $($CurrentiPilotTelephoneNumber | Format-List | Out-String)" # Number is not assigned, user doesn't have existing number assigned, assigning matching number found in iPilot if (!$CurrentiPilotTelephoneNumber) { # Assign BusinessPhone as iPilot number Write-Output "Provisioning $($iPilotUser.AzureADUserPrincipalName) with $($TelephoneNumber.TelephoneNumber)" $NewiPilotUserSplat = @{ UserPrincipalName = $iPilotUser.AzureADUserPrincipalName FirstName = $iPilotUser.AzureADFirstName LastName = $iPilotUser.AzureADLastName TelephoneNumber = $TelephoneNumber.TelephoneNumber Credential = $iPilotCredential } New-iPilotTeamsUserAssignment @NewiPilotUserSplat # Number is not assigned, user has existing number assigned, will deprovision and reprovision with new number } elseif ($CurrentiPilotTelephoneNumber.Length -eq 10 -and $TelephoneNumber.TelephoneNumber) { # Deprovision existing number Remove-iPilotTeamsUserAssignment -UserPrincipalName $iPilotUser.AzureADUserPrincipalName # Set businessPhone to be used on next sync $iPilotUser.businessPhone = $TelephoneNumber.TelephoneNumber # Add user to UsersToProvisionOnNextSync $UsersToProvisionOnNextSync += $iPilotUser } } else { # If phone number was changed and new number doesn't exist or is already assigned in iPilot, do nothing Write-Warning "Would have assigned $($TelephoneNumber.TelephoneNumber) to $($iPilotUser.AzureADUserPrincipalName) but it is already assigned to $($TelephoneNumber.ProvisionUser.upn). Doing nothing." } } else { # If phone number is present in AD and does NOT match one in iPilot, do nothing Write-Verbose "BusinessPhone attribute is present but does not match number in iPilot, skipping provisioning" } } else { # Business Phone blank, get next available number & provision user # Assign next available number Write-Verbose "$($iPilotUser.AzureADUserPrincipalName)'s BusinessPhone is blank, get next available number & provision user" $TelephoneNumber = Get-iPilotNumber -Available -NumberOfRecords 1 | Where-Object {$_.ProvisionUserStatus -eq "PENDING"} | Select-Object -First 1 if ($TelephoneNumber.Status -eq "COMPLETE" -and $TelephoneNumber.ProvisionUserStatus -eq "PENDING") { # Provision user with first available TelephoneNumber Write-Output "Provisioning $($iPilotUser.AzureADUserPrincipalName) with $($TelephoneNumber.TelephoneNumber)" $NewiPilotUserSplat = @{ UserPrincipalName = $iPilotUser.AzureADUserPrincipalName FirstName = $iPilotUser.AzureADFirstName LastName = $iPilotUser.AzureADLastName TelephoneNumber = $TelephoneNumber.TelephoneNumber Credential = $iPilotCredential } New-iPilotTeamsUserAssignment @NewiPilotUserSplat } else { # No numbers in iPilot ready to be used for for provisioning, skipping provisioning Write-Warning "No numbers in iPilot ready to be used for for provisioning, skipping provisioning for $($iPilotUser.AzureADUserPrincipalName)" } } } #endregion Provision User } #endregion Added to Group #region Update user's First/Last Name for existing users if ($iPilotUser.iPilotUserPrincipalName -and ( ($iPilotUser.AzureADFirstName -ne $iPilotUser.iPilotFirstName) -or ($iPilotUser.AzureADLastName -ne $iPilotUser.iPilotLastName) ) ) { # Splat Set-iPilotTeamsUser Write-Output "=== Updating UserID $($iPilotUser.UserID): ===`n UserPrincipalName: $($iPilotUser.iPilotUserPrincipalName)`n `NewUserPrincipalName: $($iPilotUser.AzureADUserPrincipalName)`n FirstName: $($iPilotUser.AzureADFirstName)`n LastName: $($iPilotUser.AzureADLastName)`n" # Set UPN with First and Last Name $SetiPilotUserSplat = @{ UserPrincipalName = $iPilotUser.iPilotUserPrincipalName FirstName = $iPilotUser.AzureADFirstName LastName = $iPilotUser.AzureADLastName } $SetiPilotTeamsUserResponse = Set-iPilotTeamsUser @SetiPilotUserSplat if ($SetiPilotTeamsUserResponse.statusCode -eq 200) { Get-iPilotTeamsUser -FilterByUserPrincipalName $iPilotUser.iPilotUserPrincipalName } else { throw "Failed to update User ID $($iPilotUser.UserID) - $($iPilotUser.AzureADFirstName) $($iPilotUser.AzureADLastName). Error: $($SetiPilotTeamsUserResponse.statusCode) - $($SetiPilotTeamsUserResponse.status)" } } #endregion Update user's First/Last Name for existing users #region If still in group, and number changes to unassigned number in ipilot, deprovision original number, and provision new number if ($iPilotUser.iPilotUserPrincipalName -and ($iPilotSyncGroupMembers.userPrincipalName -contains $iPilotUser.AzureADUserPrincipalName)) { # Check if number changed if ($iPilotUser.BusinessPhone -and ($iPilotBusinessPhoneNumber.TelephoneNumber -ne $iPilotUser.BusinessPhone)) { $NewTelephoneNumber = Get-iPilotNumber -FilterByTelephoneNumber $iPilotUser.BusinessPhone Write-Verbose "$($iPilotUser.AzureADUserPrincipalName) NewTelephoneNumber:`n$($NewTelephoneNumber | Format-Table -AutoSize | Out-String)" $OldTelephoneNumber = Get-iPilotNumber -FilterByTelephoneNumber $iPilotUser.ExistingiPilotTelephoneNumber Write-Verbose "$($iPilotUser.AzureADUserPrincipalName) OldTelephoneNumber:`n$($OldTelephoneNumber | Format-Table -AutoSize | Out-String)" } # If number changed if (($NewTelephoneNumber.TelephoneNumber -ne $iPilotUser.ExistingiPilotTelephoneNumber) -and $NewTelephoneNumber.ProvisionUserStatus -eq "PENDING") { Write-Output "Deprovisioning $($iPilotUser.AzureADUserPrincipalName) from $($iPilotUser.ExistingiPilotTelephoneNumber)" Remove-iPilotTeamsUserAssignment -UserPrincipalName $iPilotUser.AzureADUserPrincipalName Write-Verbose "Waiting 30 seconds..." Start-Sleep -Seconds 30 $ProvisionStatus = Get-iPilotNumber -FilterByUserPrincipalName $iPilotUser.AzureADUserPrincipalName -Verbose | Select-Object -ExpandProperty ProvisionUserStatus Write-Verbose "$($iPilotUser.AzureADUserPrincipalName) - TelephoneNumber $($iPilotUser.ExistingiPilotTelephoneNumber) ProvisionStatus: $ProvisionStatus" # Provision user if successfully deprovisioned if ($ProvisionStatus -ne "PENDING") { Write-Warning "$($iPilotUser.AzureADUserPrincipalName) is not deprovisioned. Will wait until next sync to provision." $UsersToProvisionOnNextSync += $iPilotUser } else { if ($NewTelephoneNumber.ProvisionUserStatus -eq "PENDING") { # Provision user with new phone number $NewiPilotUserSplat = @{ UserPrincipalName = $iPilotUser.AzureADUserPrincipalName FirstName = $iPilotUser.AzureADFirstName LastName = $iPilotUser.AzureADLastName TelephoneNumber = $NewTelephoneNumber.TelephoneNumber Credential = $iPilotCredential } New-iPilotTeamsUserAssignment @NewiPilotUserSplat } else { Write-Warning "$($iPilotUser.BusinessPhone) is not available in iPilot. ProvisionUserStatus: $($NewTelephoneNumber.ProvisionUserStatus)" } } } else { Write-Verbose "$($iPilotUser.AzureADUserPrincipalName) phone number has not changed" } } #endregion If still in group, and number changes to unassigned number in ipilot, deprovision original number, and provision new number } # Export UsersToProvisionOnNextSync to UsersToProvisionFromLastSync_<domain>.xml in iPilot data directory for retreival on next sync $UsersToProvisionOnNextSync | Export-Clixml "$iPilotDataDirectory\UsersToProvisionFromLastSync_$($iPilotDomain).xml" -Force #endregion Provision new users/update users with name or number changes #region Update users with UPN Changes if ($ChangedUserPrincipalNames) { Write-Output "Updating users with UserPrincipalName changes" Foreach ($iPilotUser in $ChangedUserPrincipalNames) { Write-Output "Updating UserID $($iPilotUser.UserID): UserPrincipalName: $($iPilotUser.PreviousUPN) NewUserPrincipalName: $($iPilotUser.userPrincipalName)" $SetiPilotUserSplat = @{ UserPrincipalName = $iPilotUser.PreviousUPN NewUserPrincipalName = $iPilotUser.userPrincipalName } Set-iPilotTeamsUser @SetiPilotUserSplat } } else { Write-Output "No users have UserPrincipalName changes" } #endregion Update users with UPN Changes #region Users removed from sync group $DeletedUsers = @() $DeletedUsers = $ChangedUsers | Where-Object {$_.RemovedFromiPilotSyncGroup -eq $true} # Get any users that aren't in iPilot sync group and add them to group to be deprovisioned $UserPrincipalNamesRemovedFromiPilotSyncGroup = Compare-Object -ReferenceObject $iPilotSyncGroupMembers.userPrincipalName ` -DifferenceObject $AlliPilotUsers.UserPrincipalName | ForEach-Object { $_ | Where-Object {$_.SideIndicator -eq "=>"}} | Select-Object -ExpandProperty InputObject $UsersRemovedFromiPilotSyncGroup = $ChangedUsers | Where-Object {$UserPrincipalNamesRemovedFromiPilotSyncGroup -contains $_.userPrincipalName } #$UsersRemovedFromiPilotSyncGroup = $AlliPilotUsers | Where-Object {$UserPrincipalNamesRemovedFromiPilotSyncGroup -contains $_.userPrincipalName } # Add missing attributes $UsersRemovedFromiPilotSyncGroup | ForEach-Object { Add-Member -InputObject $_ -Name BusinessPhone -MemberType NoteProperty -Value $_.TelephoneNumber -Force} $UsersRemovedFromiPilotSyncGroup | ForEach-Object { Add-Member -InputObject $_ -Name AzureADUserPrincipalName -MemberType NoteProperty -Value $_.UserPrincipalName -Force} # Output users removed from iPilot Sync Group Write-Verbose "UsersRemovedFromiPilotSyncGroup:`n$($UsersRemovedFromiPilotSyncGroup | Format-List | Out-String)" $DeletedUsers += $UsersRemovedFromiPilotSyncGroup if ($DeletedUsers) { Write-Output "Deprovisioning users removed from $iPilotSyncGroupName" Write-Verbose "DeletedUsers:`n$($DeletedUsers | Format-List | Out-String)" } else { Write-Output "No users removed from $iPilotSyncGroupName" } Foreach ($DeletedUser in $DeletedUsers) { # If phone number is present in AD and matches an assigned number in iPilot, auto deprovision user $DeletedUseriPilotBusinessPhone = Get-iPilotNumber -FilterByTelephoneNumber $DeletedUser.BusinessPhone if ($DeletedUseriPilotBusinessPhone) { Write-Output "Deprovisioning $($DeletedUser.AzureADUserPrincipalName) - user was removed from $iPilotSyncGroupName" Remove-iPilotTeamsUserAssignment -UserPrincipalName $DeletedUser.AzureADUserPrincipalName } # If phone number is not present in AD and UPN matches an assigned number in iPilot, auto deprovision user $DeletedUseriPilotUserPrincipalName = Get-iPilotNumber -FilterByUserPrincipalName $DeletedUser.AzureADUserPrincipalName if ($DeletedUseriPilotUserPrincipalName -and !$DeletedUser.businessPhones) { Write-Output "Deprovisioning $($DeletedUser.AzureADUserPrincipalName) - user was removed from $iPilotSyncGroupName and businessPhones is empty" Remove-iPilotTeamsUserAssignment -UserPrincipalName $DeletedUser.AzureADUserPrincipalName } # If phone number is present in AD and does NOT match number in iPilot, do nothing if ($DeletedUser.businessPhones -and !$DeletedUseriPilotBusinessPhone) { Write-Verbose "Phone number ($($DeletedUser.businessPhones)) is present in Azure AD and does NOT match number in iPilot, do nothing" } # If phone number is not present in AD and does NOT match UPN in iPilot, do nothing if (!$DeletedUser.businessPhones -and !$DeletedUseriPilotUserPrincipalName) { Write-Verbose "Phone number (businessPhones) is not present in Azure AD and does NOT match number in iPilot, do nothing" } } #endregion Users removed from sync group #region Disabled users - deprovision $DisabledUsers = $ChangedUsers | Where-Object {$_.accountEnable -eq $false} if ($DisabledUsers) { Write-Output "Deprovisioning users disabled in Azure AD" } else { Write-Output "No users in $iPilotSyncGroupName have been disabled in Azure AD" } Foreach ($DisabledUser in $DisabledUsers) { Write-Output "Deprovisioning $($DisabledUser.AzureADUserPrincipalName) - user disable in Azure AD" Remove-iPilotTeamsUserAssignment -UserPrincipalName $DisabledUser.AzureADUserPrincipalName } #endregion Disabled users - deprovision #endregion Perform sync #region End Logging $LogsToDelete = Get-ChildItem -Path $iPilotDataDirectory -Filter *_Invoke-iPilotUserSync.log -Depth 0 | Where-Object {$_.LastWriteTime -lt (Get-Date).AddDays(-$RetainLogsForDays)} if ($LogsToDelete) { Write-Output "Cleaning up logs older than $RetainLogsForDays in $iPilotDataDirectory" Write-Output "Deleting Log files:`n$($LogsToDelete | Format-Table | Out-String)" If ($LogsToDelete.Count -ge 1) { $LogsToDelete | Remove-Item -Force -Verbose } } # Verbose Switch if($PSBoundParameters.containskey("Verbose")) { $VerbosePreference = $PreviousVerbosePreference } # Debug Switch if($PSBoundParameters.containskey("Debug")) { $DebugPreference = $PreviousDebugPreference } Stop-Transcript -Verbose #endregion End Logging } |