Add device comparison service - compares AD managedBy vs Disco assigned user
This commit is contained in:
@@ -0,0 +1,167 @@
|
||||
using Disco.Data.Repository;
|
||||
using Disco.Models.Repository;
|
||||
using Disco.Plugins.ADCompare.Models;
|
||||
using Disco.Services.Interop.ActiveDirectory;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace Disco.Plugins.ADCompare.Features
|
||||
{
|
||||
public class DeviceCompareService
|
||||
{
|
||||
private readonly DiscoDataContext database;
|
||||
|
||||
public DeviceCompareService(DiscoDataContext database)
|
||||
{
|
||||
this.database = database;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Compare all Disco devices' assigned users against the AD computer managedBy field.
|
||||
/// Only considers active (not decommissioned) devices with a domain ID.
|
||||
/// </summary>
|
||||
public DeviceComparisonSummary CompareAllDevices()
|
||||
{
|
||||
var summary = new DeviceComparisonSummary();
|
||||
|
||||
// Load active devices that have a domain computer account
|
||||
var devices = database.Devices
|
||||
.Include("AssignedUser")
|
||||
.Where(d => d.DeviceDomainId != null && d.DecommissionedDate == null)
|
||||
.ToList();
|
||||
|
||||
summary.TotalDevices = devices.Count;
|
||||
|
||||
foreach (var device in devices)
|
||||
{
|
||||
var result = CompareDevice(device);
|
||||
summary.Results.Add(result);
|
||||
}
|
||||
|
||||
summary.DevicesWithAssignment = summary.Results.Count(r => r.HasAssignment);
|
||||
summary.DevicesNotInAD = summary.Results.Count(r => !r.FoundInAD);
|
||||
summary.DevicesMatched = summary.Results.Count(r => r.IsMatch);
|
||||
summary.DevicesMismatched = summary.Results.Count(r => !r.IsMatch && r.FoundInAD);
|
||||
summary.DevicesNoAssignment = summary.Results.Count(r => !r.HasAssignment);
|
||||
summary.DevicesNoManagedBy = summary.Results.Count(r => r.FoundInAD && !r.HasManagedBy);
|
||||
summary.DevicesADDisabled = summary.Results.Count(r => r.ADAccountDisabled);
|
||||
|
||||
return summary;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Compare a single device's Disco assignment against AD managedBy.
|
||||
/// </summary>
|
||||
public DeviceComparisonResult CompareDevice(Device device)
|
||||
{
|
||||
var result = new DeviceComparisonResult
|
||||
{
|
||||
SerialNumber = device.SerialNumber,
|
||||
DeviceDomainId = device.DeviceDomainId,
|
||||
ComputerName = device.ComputerName,
|
||||
DiscoAssignedUserId = device.AssignedUserId,
|
||||
DiscoAssignedUserDisplayName = device.AssignedUser?.DisplayName,
|
||||
HasAssignment = !string.IsNullOrEmpty(device.AssignedUserId)
|
||||
};
|
||||
|
||||
try
|
||||
{
|
||||
// Look up the computer in AD, requesting the managedBy attribute
|
||||
var adAccount = ActiveDirectory.RetrieveADMachineAccount(device.DeviceDomainId, new[] { "managedBy" });
|
||||
|
||||
if (adAccount == null)
|
||||
{
|
||||
result.FoundInAD = false;
|
||||
result.MismatchReason = "Computer not found in AD";
|
||||
return result;
|
||||
}
|
||||
|
||||
result.FoundInAD = true;
|
||||
result.ADAccountDisabled = adAccount.IsDisabled;
|
||||
|
||||
// Get the managedBy DN from AD
|
||||
var managedByDN = adAccount.GetPropertyValue<string>("managedBy");
|
||||
result.ADManagedByDN = managedByDN;
|
||||
result.HasManagedBy = !string.IsNullOrEmpty(managedByDN);
|
||||
|
||||
// Resolve managedBy DN to a DOMAIN\username
|
||||
if (result.HasManagedBy)
|
||||
{
|
||||
try
|
||||
{
|
||||
var managedByUser = ActiveDirectory.RetrieveADUserAccount(managedByDN);
|
||||
if (managedByUser != null)
|
||||
{
|
||||
result.ADManagedByUserId = managedByUser.Id;
|
||||
result.ADManagedByDisplayName = managedByUser.DisplayName;
|
||||
}
|
||||
else
|
||||
{
|
||||
result.ADManagedByUserId = managedByDN; // fallback to DN
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
// If we can't resolve the DN, store it raw
|
||||
result.ADManagedByUserId = managedByDN;
|
||||
}
|
||||
}
|
||||
|
||||
// Now compare
|
||||
result.IsMatch = DetermineMatch(result);
|
||||
if (!result.IsMatch)
|
||||
{
|
||||
result.MismatchReason = DetermineMismatchReason(result);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
result.FoundInAD = false;
|
||||
result.MismatchReason = $"AD lookup error: {ex.Message}";
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determine if the Disco assignment matches the AD managedBy.
|
||||
/// </summary>
|
||||
private bool DetermineMatch(DeviceComparisonResult result)
|
||||
{
|
||||
// Both empty = match (neither has an assignment)
|
||||
if (!result.HasAssignment && !result.HasManagedBy)
|
||||
return true;
|
||||
|
||||
// One has assignment, other doesn't = mismatch
|
||||
if (result.HasAssignment != result.HasManagedBy)
|
||||
return false;
|
||||
|
||||
// Both have values - compare the user IDs (case-insensitive)
|
||||
return string.Equals(
|
||||
result.DiscoAssignedUserId,
|
||||
result.ADManagedByUserId,
|
||||
StringComparison.OrdinalIgnoreCase);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Generate a human-readable reason for the mismatch.
|
||||
/// </summary>
|
||||
private string DetermineMismatchReason(DeviceComparisonResult result)
|
||||
{
|
||||
if (!result.FoundInAD)
|
||||
return "Computer not found in AD";
|
||||
|
||||
if (result.HasAssignment && !result.HasManagedBy)
|
||||
return "Assigned in Disco but AD managedBy is empty";
|
||||
|
||||
if (!result.HasAssignment && result.HasManagedBy)
|
||||
return "Not assigned in Disco but AD managedBy is set";
|
||||
|
||||
if (result.HasAssignment && result.HasManagedBy)
|
||||
return $"Different users: Disco={result.DiscoAssignedUserId}, AD managedBy={result.ADManagedByUserId}";
|
||||
|
||||
return "Unknown mismatch";
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user