Decommission AD descriptions #96
This commit is contained in:
@@ -80,7 +80,7 @@ namespace Disco.Services
|
|||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
public static void OnDecommission(this Device d, Disco.Models.Repository.DecommissionReasons Reason)
|
public static void OnDecommission(this Device d, DecommissionReasons Reason)
|
||||||
{
|
{
|
||||||
if (!d.CanDecommission())
|
if (!d.CanDecommission())
|
||||||
throw new InvalidOperationException("Decommission of Device is Denied");
|
throw new InvalidOperationException("Decommission of Device is Denied");
|
||||||
@@ -95,6 +95,7 @@ namespace Disco.Services
|
|||||||
if (adAccount != null && !adAccount.IsCriticalSystemObject)
|
if (adAccount != null && !adAccount.IsCriticalSystemObject)
|
||||||
{
|
{
|
||||||
adAccount.DisableAccount();
|
adAccount.DisableAccount();
|
||||||
|
adAccount.SetDescription(d);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -122,6 +123,7 @@ namespace Disco.Services
|
|||||||
if (adAccount != null && !adAccount.IsCriticalSystemObject)
|
if (adAccount != null && !adAccount.IsCriticalSystemObject)
|
||||||
{
|
{
|
||||||
adAccount.EnableAccount();
|
adAccount.EnableAccount();
|
||||||
|
adAccount.SetDescription(d);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -322,6 +322,7 @@
|
|||||||
<Compile Include="Interop\ActiveDirectory\ActiveDirectoryExtensions.cs" />
|
<Compile Include="Interop\ActiveDirectory\ActiveDirectoryExtensions.cs" />
|
||||||
<Compile Include="Interop\ActiveDirectory\ActiveDirectoryGroupCache.cs" />
|
<Compile Include="Interop\ActiveDirectory\ActiveDirectoryGroupCache.cs" />
|
||||||
<Compile Include="Interop\ActiveDirectory\ActiveDirectoryManagedGroups.cs" />
|
<Compile Include="Interop\ActiveDirectory\ActiveDirectoryManagedGroups.cs" />
|
||||||
|
<Compile Include="Interop\ActiveDirectory\ADDeviceDescriptionUpdateTask.cs" />
|
||||||
<Compile Include="Interop\ActiveDirectory\ADDirectoryEntry.cs" />
|
<Compile Include="Interop\ActiveDirectory\ADDirectoryEntry.cs" />
|
||||||
<Compile Include="Interop\ActiveDirectory\ADDiscoverForestServers.cs" />
|
<Compile Include="Interop\ActiveDirectory\ADDiscoverForestServers.cs" />
|
||||||
<Compile Include="Interop\ActiveDirectory\ADDomain.cs" />
|
<Compile Include="Interop\ActiveDirectory\ADDomain.cs" />
|
||||||
|
|||||||
@@ -0,0 +1,138 @@
|
|||||||
|
using Disco.Data.Repository;
|
||||||
|
using Disco.Services.Tasks;
|
||||||
|
using Quartz;
|
||||||
|
using System;
|
||||||
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
|
|
||||||
|
namespace Disco.Services.Interop.ActiveDirectory
|
||||||
|
{
|
||||||
|
public class ADDeviceDescriptionUpdateTask : ScheduledTask
|
||||||
|
{
|
||||||
|
public override string TaskName { get { return "Active Directory Device Description Update"; } }
|
||||||
|
|
||||||
|
public override bool SingleInstanceTask { get { return true; } }
|
||||||
|
public override bool CancelInitiallySupported { get { return false; } }
|
||||||
|
|
||||||
|
#region Required Helpers
|
||||||
|
private static string RequiredFilePath(DiscoDataContext Database)
|
||||||
|
{
|
||||||
|
if (Database.DiscoConfiguration.DataStoreLocation != null)
|
||||||
|
return Path.Combine(Database.DiscoConfiguration.DataStoreLocation, "_ADDeviceDescriptionUpdateRequired.txt");
|
||||||
|
else
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static bool IsRequired(DiscoDataContext Database)
|
||||||
|
{
|
||||||
|
var requiredFilePath = RequiredFilePath(Database);
|
||||||
|
|
||||||
|
if (requiredFilePath == null)
|
||||||
|
return false;
|
||||||
|
else
|
||||||
|
return File.Exists(requiredFilePath);
|
||||||
|
}
|
||||||
|
public static void SetRequired(DiscoDataContext Database)
|
||||||
|
{
|
||||||
|
var requiredFilePath = RequiredFilePath(Database);
|
||||||
|
|
||||||
|
if (requiredFilePath != null)
|
||||||
|
{
|
||||||
|
File.WriteAllText(requiredFilePath, "This file exists to indicate an update to AD Device Descriptions is required. It will automatically be deleted when the update completes.");
|
||||||
|
File.SetAttributes(requiredFilePath, FileAttributes.Hidden);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
public override void InitalizeScheduledTask(DiscoDataContext Database)
|
||||||
|
{
|
||||||
|
if (IsRequired(Database))
|
||||||
|
{
|
||||||
|
// Schedule in 5mins
|
||||||
|
var trigger = TriggerBuilder.Create()
|
||||||
|
.StartAt(DateTimeOffset.Now.AddMinutes(3));
|
||||||
|
|
||||||
|
this.ScheduleTask(trigger);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static ScheduledTaskStatus ScheduleImmediately()
|
||||||
|
{
|
||||||
|
var existingTask = ScheduledTasks.GetTaskStatuses(typeof(ADDeviceDescriptionUpdateTask)).Where(s => s.IsRunning).FirstOrDefault();
|
||||||
|
if (existingTask != null)
|
||||||
|
return existingTask;
|
||||||
|
|
||||||
|
var instance = new ADDeviceDescriptionUpdateTask();
|
||||||
|
return instance.ScheduleTask();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void ExecuteTask()
|
||||||
|
{
|
||||||
|
using (DiscoDataContext database = new DiscoDataContext())
|
||||||
|
{
|
||||||
|
Status.UpdateStatus(0, "Updating Active Directory Device Descriptions", "Reading Devices");
|
||||||
|
|
||||||
|
// Devices
|
||||||
|
var devices = database.Devices.Where(d => d.DeviceDomainId != null)
|
||||||
|
.ToList();
|
||||||
|
|
||||||
|
int failedTotal = 0;
|
||||||
|
int notFoundTotal = 0;
|
||||||
|
int completedTotal = 0;
|
||||||
|
|
||||||
|
// Refine valid devices
|
||||||
|
devices = devices.Where(d => ActiveDirectory.IsValidDomainAccountId(d.DeviceDomainId)).ToList();
|
||||||
|
|
||||||
|
foreach (var domainGroup in devices.GroupBy(d => d.ComputerDomainName).ToList())
|
||||||
|
{
|
||||||
|
ADDomain domain;
|
||||||
|
if (domainGroup.Key != null && ActiveDirectory.Context.TryGetDomainByNetBiosName(domainGroup.Key, out domain))
|
||||||
|
{
|
||||||
|
var controller = domain.GetAvailableDomainController(RequireWritable: true);
|
||||||
|
|
||||||
|
foreach (var device in domainGroup)
|
||||||
|
{
|
||||||
|
completedTotal++;
|
||||||
|
if ((completedTotal % 10) == 0)
|
||||||
|
{
|
||||||
|
Status.UpdateStatus((100D / devices.Count) * completedTotal, $"Processing: {device.DeviceDomainId} ({device.SerialNumber})");
|
||||||
|
}
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var adAccount = device.ActiveDirectoryAccount();
|
||||||
|
if (adAccount == null)
|
||||||
|
{
|
||||||
|
notFoundTotal++;
|
||||||
|
|
||||||
|
if (!device.DecommissionedDate.HasValue)
|
||||||
|
{
|
||||||
|
Status.LogWarning($"Unable to locate [{device.DeviceDomainId}] for commissioned device [{device.SerialNumber}] in the domain");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
adAccount.SetDescription(controller, device);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
failedTotal++;
|
||||||
|
Status.LogWarning($"Error when setting description of computer account [{device.DeviceDomainId}] for device [{device.SerialNumber}]: [{ex.GetType().Name}] {ex.Message}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Finished - Remove Placeholder File
|
||||||
|
var requiredFilePath = RequiredFilePath(database);
|
||||||
|
if (requiredFilePath != null && File.Exists(requiredFilePath))
|
||||||
|
File.Delete(requiredFilePath);
|
||||||
|
|
||||||
|
Status.SetFinishedMessage($"Finished updating device descriptions for {devices.Count:N0}. {notFoundTotal:N0} were not found. {failedTotal:N0} failed.");
|
||||||
|
Status.LogInformation(Status.FinishedMessage);
|
||||||
|
Status.Finished();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -3,6 +3,7 @@ using System;
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Security.Principal;
|
using System.Security.Principal;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
namespace Disco.Services.Interop.ActiveDirectory
|
namespace Disco.Services.Interop.ActiveDirectory
|
||||||
{
|
{
|
||||||
@@ -178,15 +179,18 @@ namespace Disco.Services.Interop.ActiveDirectory
|
|||||||
using (var deAccount = WritableDomainController.RetrieveDirectoryEntry(this.DistinguishedName))
|
using (var deAccount = WritableDomainController.RetrieveDirectoryEntry(this.DistinguishedName))
|
||||||
{
|
{
|
||||||
var descriptionProp = deAccount.Entry.Properties["description"];
|
var descriptionProp = deAccount.Entry.Properties["description"];
|
||||||
if (descriptionProp.Count > 0)
|
if (descriptionProp.Count != 1 || (descriptionProp[0] as string) != Description)
|
||||||
{
|
{
|
||||||
descriptionProp.Clear();
|
if (descriptionProp.Count > 0)
|
||||||
|
{
|
||||||
|
descriptionProp.Clear();
|
||||||
|
}
|
||||||
|
if (!string.IsNullOrEmpty(Description))
|
||||||
|
{
|
||||||
|
descriptionProp.Add(Description);
|
||||||
|
}
|
||||||
|
deAccount.Entry.CommitChanges();
|
||||||
}
|
}
|
||||||
if (!string.IsNullOrEmpty(Description))
|
|
||||||
{
|
|
||||||
descriptionProp.Add(Description);
|
|
||||||
}
|
|
||||||
deAccount.Entry.CommitChanges();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
public void SetDescription(string Description)
|
public void SetDescription(string Description)
|
||||||
@@ -196,19 +200,28 @@ namespace Disco.Services.Interop.ActiveDirectory
|
|||||||
|
|
||||||
public void SetDescription(ADDomainController WritableDomainController, Device Device)
|
public void SetDescription(ADDomainController WritableDomainController, Device Device)
|
||||||
{
|
{
|
||||||
System.Text.StringBuilder descriptionBuilder = new System.Text.StringBuilder();
|
var descriptionBuilder = new StringBuilder();
|
||||||
|
|
||||||
if (Device.AssignedUserId != null)
|
if (Device.DecommissionedDate.HasValue)
|
||||||
{
|
{
|
||||||
descriptionBuilder.Append(Device.AssignedUser.UserId).Append(" (").Append(Device.AssignedUser.DisplayName).Append("); ");
|
descriptionBuilder.Append("Decommissioned: ")
|
||||||
|
.Append(Device.DecommissionReason.ReasonMessage())
|
||||||
|
.Append(" (").Append(Device.DecommissionedDate.Value.ToString("yyyy-MM-dd")).Append(')');
|
||||||
}
|
}
|
||||||
|
else
|
||||||
if (Device.DeviceModelId.HasValue)
|
|
||||||
{
|
{
|
||||||
descriptionBuilder.Append(Device.DeviceModel.Description).Append("; ");
|
if (Device.AssignedUserId != null)
|
||||||
}
|
{
|
||||||
|
descriptionBuilder.Append(Device.AssignedUser.UserId).Append(" (").Append(Device.AssignedUser.DisplayName).Append("); ");
|
||||||
|
}
|
||||||
|
|
||||||
descriptionBuilder.Append(Device.DeviceProfile.Description).Append(";");
|
if (Device.DeviceModelId.HasValue)
|
||||||
|
{
|
||||||
|
descriptionBuilder.Append(Device.DeviceModel.Description).Append("; ");
|
||||||
|
}
|
||||||
|
|
||||||
|
descriptionBuilder.Append(Device.DeviceProfile.Description).Append(";");
|
||||||
|
}
|
||||||
|
|
||||||
string description = descriptionBuilder.ToString().Trim();
|
string description = descriptionBuilder.ToString().Trim();
|
||||||
if (description.Length > 1024)
|
if (description.Length > 1024)
|
||||||
@@ -336,7 +349,7 @@ namespace Disco.Services.Interop.ActiveDirectory
|
|||||||
{
|
{
|
||||||
i.Entry.UsePropertyCache = false;
|
i.Entry.UsePropertyCache = false;
|
||||||
i.Entry.MoveTo(ou.Entry);
|
i.Entry.MoveTo(ou.Entry);
|
||||||
|
|
||||||
// Update Distinguished Name
|
// Update Distinguished Name
|
||||||
this.DistinguishedName = i.Entry.Properties["distinguishedName"][0].ToString();
|
this.DistinguishedName = i.Entry.Properties["distinguishedName"][0].ToString();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -114,6 +114,10 @@ namespace Disco.Web
|
|||||||
// Attachment PDF Thumbnail Update
|
// Attachment PDF Thumbnail Update
|
||||||
if (PreviousVersion != null && PreviousVersion < new Version(2, 2, 0, 0))
|
if (PreviousVersion != null && PreviousVersion < new Version(2, 2, 0, 0))
|
||||||
Services.Documents.AttachmentImport.ThumbnailUpdateTask.SetRequired(Database);
|
Services.Documents.AttachmentImport.ThumbnailUpdateTask.SetRequired(Database);
|
||||||
|
|
||||||
|
// AD Device Description Update
|
||||||
|
if (PreviousVersion != null && PreviousVersion < new Version(2, 2, 16281, 0))
|
||||||
|
Services.Interop.ActiveDirectory.ADDeviceDescriptionUpdateTask.SetRequired(Database);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void DisposeEnvironment()
|
public static void DisposeEnvironment()
|
||||||
|
|||||||
@@ -33,6 +33,14 @@ namespace Disco.Web.Areas.API.Controllers
|
|||||||
return RedirectToAction(MVC.Config.Logging.TaskStatus(ts.SessionId));
|
return RedirectToAction(MVC.Config.Logging.TaskStatus(ts.SessionId));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[DiscoAuthorize(Claims.DiscoAdminAccount)]
|
||||||
|
public virtual ActionResult UpdateADDeviceDescriptions()
|
||||||
|
{
|
||||||
|
var ts = Disco.Services.Interop.ActiveDirectory.ADDeviceDescriptionUpdateTask.ScheduleImmediately();
|
||||||
|
ts.SetFinishedUrl(Url.Action(MVC.Config.SystemConfig.Index()));
|
||||||
|
return RedirectToAction(MVC.Config.Logging.TaskStatus(ts.SessionId));
|
||||||
|
}
|
||||||
|
|
||||||
[DiscoAuthorize(Claims.Config.System.Show)]
|
[DiscoAuthorize(Claims.Config.System.Show)]
|
||||||
public virtual ActionResult UpdateCheck()
|
public virtual ActionResult UpdateCheck()
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -147,6 +147,7 @@ namespace Disco.Web.Areas.API.Controllers
|
|||||||
{
|
{
|
||||||
public readonly string UpdateLastNetworkLogonDates = "UpdateLastNetworkLogonDates";
|
public readonly string UpdateLastNetworkLogonDates = "UpdateLastNetworkLogonDates";
|
||||||
public readonly string UpdateAttachmentThumbnails = "UpdateAttachmentThumbnails";
|
public readonly string UpdateAttachmentThumbnails = "UpdateAttachmentThumbnails";
|
||||||
|
public readonly string UpdateADDeviceDescriptions = "UpdateADDeviceDescriptions";
|
||||||
public readonly string UpdateCheck = "UpdateCheck";
|
public readonly string UpdateCheck = "UpdateCheck";
|
||||||
public readonly string UpdateOrganisationName = "UpdateOrganisationName";
|
public readonly string UpdateOrganisationName = "UpdateOrganisationName";
|
||||||
public readonly string OrganisationLogo = "OrganisationLogo";
|
public readonly string OrganisationLogo = "OrganisationLogo";
|
||||||
@@ -169,6 +170,7 @@ namespace Disco.Web.Areas.API.Controllers
|
|||||||
{
|
{
|
||||||
public const string UpdateLastNetworkLogonDates = "UpdateLastNetworkLogonDates";
|
public const string UpdateLastNetworkLogonDates = "UpdateLastNetworkLogonDates";
|
||||||
public const string UpdateAttachmentThumbnails = "UpdateAttachmentThumbnails";
|
public const string UpdateAttachmentThumbnails = "UpdateAttachmentThumbnails";
|
||||||
|
public const string UpdateADDeviceDescriptions = "UpdateADDeviceDescriptions";
|
||||||
public const string UpdateCheck = "UpdateCheck";
|
public const string UpdateCheck = "UpdateCheck";
|
||||||
public const string UpdateOrganisationName = "UpdateOrganisationName";
|
public const string UpdateOrganisationName = "UpdateOrganisationName";
|
||||||
public const string OrganisationLogo = "OrganisationLogo";
|
public const string OrganisationLogo = "OrganisationLogo";
|
||||||
@@ -349,6 +351,17 @@ namespace Disco.Web.Areas.API.Controllers
|
|||||||
return callInfo;
|
return callInfo;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[NonAction]
|
||||||
|
partial void UpdateADDeviceDescriptionsOverride(T4MVC_System_Web_Mvc_ActionResult callInfo);
|
||||||
|
|
||||||
|
[NonAction]
|
||||||
|
public override System.Web.Mvc.ActionResult UpdateADDeviceDescriptions()
|
||||||
|
{
|
||||||
|
var callInfo = new T4MVC_System_Web_Mvc_ActionResult(Area, Name, ActionNames.UpdateADDeviceDescriptions);
|
||||||
|
UpdateADDeviceDescriptionsOverride(callInfo);
|
||||||
|
return callInfo;
|
||||||
|
}
|
||||||
|
|
||||||
[NonAction]
|
[NonAction]
|
||||||
partial void UpdateCheckOverride(T4MVC_System_Web_Mvc_ActionResult callInfo);
|
partial void UpdateCheckOverride(T4MVC_System_Web_Mvc_ActionResult callInfo);
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user