internal string SyncRoles(int portalId, AzureConfig settings)
        {
            try
            {
                var syncErrorsDesc     = "";
                var syncErrors         = 0;
                var groupsCreated      = 0;
                var groupsDeleted      = 0;
                var customRoleMappings = GetRoleMappingsForPortal(portalId, settings);

                if (string.IsNullOrEmpty(settings.AADApplicationId) || string.IsNullOrEmpty(settings.AADApplicationKey))
                {
                    throw new Exception($"AAD application ID or key are not valid on portal {portalId}");
                }

                var graphClient = new GraphClient(settings.AADApplicationId, settings.AADApplicationKey, settings.TenantId);
                var query       = "$orderby=displayName";
                var filter      = ConfigurationManager.AppSettings["AzureAD.GetAllGroups.Filter"];
                if (!string.IsNullOrEmpty(filter))
                {
                    query = $"$filter={filter}";
                }
                // Add roles from AAD
                var aadGroups    = graphClient.GetAllGroups(query);
                var allaadGroups = new List <Components.Graph.Models.Group>();
                if (aadGroups != null && aadGroups.Values != null)
                {
                    var groupPrefix = settings.GroupNamePrefixEnabled ? "Azure-" : "";
                    while (aadGroups.Values.Count > 0)
                    {
                        var groups = aadGroups.Values;
                        allaadGroups.AddRange(groups);
                        if (customRoleMappings != null && customRoleMappings.Count > 0)
                        {
                            groupPrefix = "";
                            var b2cRoles = customRoleMappings.Select(rm => rm.AadRoleName);
                            groups.RemoveAll(x => !b2cRoles.Contains(x.DisplayName));
                        }

                        foreach (var aadGroup in groups)
                        {
                            var dnnRole = RoleController.Instance.GetRoleByName(portalId, $"{groupPrefix}{aadGroup.DisplayName}");
                            if (dnnRole == null)
                            {
                                try
                                {
                                    // Create role
                                    var roleId = AddRole(portalId, $"{groupPrefix}{aadGroup.DisplayName}", aadGroup.Description, true);
                                    groupsCreated++;
                                }
                                catch (Exception ex)
                                {
                                    syncErrors++;
                                    syncErrorsDesc += $"\n{ex.Message}";
                                }
                            }
                        }

                        if (string.IsNullOrEmpty(aadGroups.ODataNextLink))
                        {
                            break;
                        }
                        aadGroups = graphClient.GetNextGroups(aadGroups.ODataNextLink);
                    }
                }
                // Let's remove DNN roles imported from B2C that no longer exists in AAD
                var dnnAadRoles = GetDnnAadRoles(portalId);
                // Let's exclude first the mapped roles (we won't remove mapped roles)
                foreach (var role in customRoleMappings)
                {
                    var mappedRole = dnnAadRoles.FirstOrDefault(ri => ri.RoleName == role.DnnRoleName);
                    if (mappedRole != null)
                    {
                        dnnAadRoles.Remove(mappedRole);
                    }
                }
                // Remove roles no longer exists in AAD (only the ones that are not mapped against a DNN role)
                foreach (var dnnRole in dnnAadRoles)
                {
                    if (allaadGroups.Count == 0 ||
                        allaadGroups.FirstOrDefault(x => x.DisplayName == (dnnRole.RoleName.StartsWith("Azure-") ? dnnRole.RoleName.Substring("Azure-".Length) : dnnRole.RoleName)) == null)
                    {
                        try
                        {
                            RoleController.Instance.DeleteRole(dnnRole);
                            // This is a workaround to a bug in DNN where RoleSettings is not deleted when a role is deleted
                            DotNetNuke.Data.DataContext.Instance().Execute(System.Data.CommandType.Text, $"DELETE {DotNetNuke.Data.DataProvider.Instance().DatabaseOwner}{DotNetNuke.Data.DataProvider.Instance().ObjectQualifier}RoleSettings WHERE RoleID = @0", dnnRole.RoleID);
                            groupsDeleted++;
                        }
                        catch (Exception ex)
                        {
                            syncErrors++;
                            syncErrorsDesc += $"\n{ex.Message}";
                        }
                    }
                }

                var syncResultDesc = "";
                if (!string.IsNullOrEmpty(syncErrorsDesc))
                {
                    Logger.Error($"AAD Role Sync errors detected: {syncErrorsDesc}");
                    syncResultDesc = $"Portal {portalId} synced with errors, check logs for more information (sync errors: {syncErrors}; groups created: {groupsCreated}; groups deleted: {groupsDeleted}";
                }
                else
                {
                    syncResultDesc = $"Successfully synchronized portal {portalId} (sync errors: 0; groups created: {groupsCreated}; groups deleted: {groupsDeleted}";
                }
                return(syncResultDesc);
            }
            catch (Exception e)
            {
                Logger.Error($"Error while synchronizing the roles from portal {portalId}: {e}");
                return($"Error while synchronizing the roles from portal {portalId}: {e}");
            }
        }