Update #42: AD Migration
Refactor to target specific Domain Controllers, with failover.
This commit is contained in:
@@ -1,7 +1,4 @@
|
||||
using Disco.Models.Interop.ActiveDirectory;
|
||||
using Disco.Models.Repository;
|
||||
using Disco.Services.Interop.ActiveDirectory.Internal;
|
||||
using System;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.DirectoryServices;
|
||||
using System.DirectoryServices.ActiveDirectory;
|
||||
@@ -16,23 +13,7 @@ namespace Disco.Services.Interop.ActiveDirectory
|
||||
{
|
||||
#region Domain/Directory Extensions
|
||||
|
||||
public static DomainController RetrieveWritableDomainController(this ActiveDirectoryDomain domain)
|
||||
{
|
||||
return ADInterop.RetrieveWritableDomainController(domain);
|
||||
}
|
||||
|
||||
public static IEnumerable<DomainController> RetrieveReachableDomainControllers(this ActiveDirectorySite site, ActiveDirectoryDomain domain)
|
||||
{
|
||||
return site.Servers.OfType<DomainController>().Where(dc => dc.Reachable() && dc.Domain.Name.Equals(domain.DnsName));
|
||||
}
|
||||
|
||||
public static IEnumerable<DomainController> RetrieveReachableDomainControllers(this ActiveDirectoryDomain domain)
|
||||
{
|
||||
var d = Domain.GetDomain(new DirectoryContext(DirectoryContextType.Domain, domain.DnsName));
|
||||
return d.FindAllDomainControllers().OfType<DomainController>().Where(dc => dc.Reachable());
|
||||
}
|
||||
|
||||
public static bool Reachable(this DirectoryServer ds)
|
||||
public static bool IsReachable(this DirectoryServer ds)
|
||||
{
|
||||
using (Ping p = new Ping())
|
||||
{
|
||||
@@ -41,351 +22,82 @@ namespace Disco.Services.Interop.ActiveDirectory
|
||||
}
|
||||
}
|
||||
|
||||
public static string GetFriendlyOrganisationalUnitName(this ActiveDirectoryDomain domain, string DistinguishedName)
|
||||
public static IEnumerable<DomainController> WhereReachable(this DomainControllerCollection domainControllers)
|
||||
{
|
||||
if (!DistinguishedName.EndsWith(domain.DistinguishedName, StringComparison.InvariantCultureIgnoreCase))
|
||||
throw new ArgumentException(string.Format("The Distinguished Name [{0}] doesn't exist within this domain [{1}]", DistinguishedName, domain.DistinguishedName));
|
||||
|
||||
StringBuilder name = new StringBuilder();
|
||||
|
||||
name.Append('[').Append(domain.NetBiosName).Append(']');
|
||||
|
||||
var subDN = DistinguishedName.Substring(0, DistinguishedName.Length - domain.DistinguishedName.Length);
|
||||
var subDNComponents = subDN.Split(',');
|
||||
|
||||
subDNComponents
|
||||
.Where(c => !string.IsNullOrWhiteSpace(c))
|
||||
.Reverse()
|
||||
.Select(c => c.Substring(c.IndexOf('=') + 1))
|
||||
.ToList()
|
||||
.ForEach(c => name.Append(" > ").Append(c));
|
||||
|
||||
return name.ToString();
|
||||
return domainControllers.Cast<DomainController>().Where(dc => dc.IsReachable());
|
||||
}
|
||||
|
||||
public static string GetDefaultComputerContainer(this ActiveDirectoryDomain domain)
|
||||
public static IEnumerable<ADDomainController> WhereReachable(this IEnumerable<ADDomainController> domainControllers)
|
||||
{
|
||||
return string.Format("CN=Computers,{0}", domain.DistinguishedName);
|
||||
return domainControllers.Where(dc => dc.DomainController.IsReachable());
|
||||
}
|
||||
|
||||
// Directory Entry Properties (Generic Helpers)
|
||||
public static T Value<T>(this PropertyCollection properties, string PropertyName)
|
||||
{
|
||||
var p = properties.Values<T>(PropertyName);
|
||||
return p.FirstOrDefault();
|
||||
}
|
||||
public static IEnumerable<T> Values<T>(this PropertyCollection properties, string PropertyName)
|
||||
{
|
||||
var p = properties[PropertyName];
|
||||
return p.OfType<T>();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region User Account Extensions
|
||||
public static object GetPropertyValue(this ActiveDirectoryUserAccount account, string PropertyName, int Index = 0)
|
||||
#region ADObject Builders
|
||||
|
||||
// User Accounts
|
||||
public static ADUserAccount AsADUserAccount(this ADSearchResult SearchResult, bool Quick, string[] AdditionalProperties)
|
||||
{
|
||||
switch (PropertyName.ToLower())
|
||||
{
|
||||
case "name":
|
||||
return account.Name;
|
||||
case "samaccountname":
|
||||
return account.SamAccountName;
|
||||
case "distinguishedname":
|
||||
return account.DistinguishedName;
|
||||
case "objectsid":
|
||||
return account.SecurityIdentifier;
|
||||
case "sn":
|
||||
return account.Surname;
|
||||
case "givenname":
|
||||
return account.GivenName;
|
||||
case "mail":
|
||||
return account.Email;
|
||||
case "telephonenumber":
|
||||
return account.Phone;
|
||||
default:
|
||||
object[] adProperty;
|
||||
if (account.LoadedProperties.TryGetValue(PropertyName, out adProperty) && Index <= adProperty.Length)
|
||||
return adProperty[Index];
|
||||
else
|
||||
return null;
|
||||
}
|
||||
return ADUserAccount.FromSearchResult(SearchResult, Quick, AdditionalProperties);
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Machine Account Extensions
|
||||
|
||||
public static void DeleteAccount(this ActiveDirectoryMachineAccount account, DomainController DomainController)
|
||||
public static IEnumerable<ADUserAccount> AsADUserAccounts(this IEnumerable<ADSearchResult> SearchResults, bool Quick, string[] AdditionalProperties)
|
||||
{
|
||||
if (account.IsCriticalSystemObject)
|
||||
throw new InvalidOperationException(string.Format("This account {0} is a Critical System Active Directory Object and Disco refuses to modify it", account.DistinguishedName));
|
||||
|
||||
using (DirectoryEntry deAccount = DomainController.RetrieveDirectoryEntry(account.DistinguishedName))
|
||||
{
|
||||
deAccount.DeleteObjectRecursive();
|
||||
}
|
||||
}
|
||||
public static void DeleteAccount(this ActiveDirectoryMachineAccount account)
|
||||
{
|
||||
var domain = account.GetDomain();
|
||||
|
||||
using (var domainController = domain.RetrieveWritableDomainController())
|
||||
{
|
||||
account.DeleteAccount(domainController);
|
||||
}
|
||||
return SearchResults.Select(sr => ADUserAccount.FromSearchResult(sr, Quick, AdditionalProperties));
|
||||
}
|
||||
|
||||
private static void SetNetbootGUID(this ActiveDirectoryMachineAccount account, DomainController DomainController, System.Guid updatedNetbootGUID)
|
||||
// Machine Accounts
|
||||
public static ADMachineAccount AsADMachineAccount(this ADSearchResult SearchResult, string[] AdditionalProperties)
|
||||
{
|
||||
if (account.IsCriticalSystemObject)
|
||||
throw new InvalidOperationException(string.Format("This account {0} is a Critical System Active Directory Object and Disco refuses to modify it", account.DistinguishedName));
|
||||
|
||||
using (DirectoryEntry deAccount = DomainController.RetrieveDirectoryEntry(account.DistinguishedName))
|
||||
{
|
||||
PropertyValueCollection netbootGUIDProp = deAccount.Properties["netbootGUID"];
|
||||
bool flag = netbootGUIDProp.Count > 0;
|
||||
if (flag)
|
||||
{
|
||||
netbootGUIDProp.Clear();
|
||||
}
|
||||
netbootGUIDProp.Add(updatedNetbootGUID.ToByteArray());
|
||||
deAccount.CommitChanges();
|
||||
}
|
||||
return ADMachineAccount.FromSearchResult(SearchResult, AdditionalProperties);
|
||||
}
|
||||
public static void SetDescription(this ActiveDirectoryMachineAccount account, DomainController DomainController, string Description)
|
||||
public static IEnumerable<ADMachineAccount> AsADMachineAccounts(this IEnumerable<ADSearchResult> SearchResults, string[] AdditionalProperties)
|
||||
{
|
||||
using (DirectoryEntry deAccount = DomainController.RetrieveDirectoryEntry(account.DistinguishedName))
|
||||
{
|
||||
PropertyValueCollection descriptionProp = deAccount.Properties["description"];
|
||||
if (descriptionProp.Count > 0)
|
||||
{
|
||||
descriptionProp.Clear();
|
||||
}
|
||||
if (!string.IsNullOrEmpty(Description))
|
||||
{
|
||||
descriptionProp.Add(Description);
|
||||
}
|
||||
deAccount.CommitChanges();
|
||||
}
|
||||
}
|
||||
public static void SetDescription(this ActiveDirectoryMachineAccount account, string Description)
|
||||
{
|
||||
var domain = account.GetDomain();
|
||||
|
||||
using (var domainController = domain.RetrieveWritableDomainController())
|
||||
{
|
||||
account.SetDescription(domainController, Description);
|
||||
}
|
||||
return SearchResults.Select(sr => ADMachineAccount.FromSearchResult(sr, AdditionalProperties));
|
||||
}
|
||||
|
||||
public static void SetDescription(this ActiveDirectoryMachineAccount account, DomainController DomainController, Device Device)
|
||||
// Groups
|
||||
public static ADGroup AsADGroup(this ADSearchResult SearchResult)
|
||||
{
|
||||
System.Text.StringBuilder descriptionBuilder = new System.Text.StringBuilder();
|
||||
|
||||
if (Device.AssignedUserId != null)
|
||||
{
|
||||
descriptionBuilder.Append(Device.AssignedUser.UserId).Append(" (").Append(Device.AssignedUser.DisplayName).Append("); ");
|
||||
}
|
||||
|
||||
if (Device.DeviceModelId.HasValue)
|
||||
{
|
||||
descriptionBuilder.Append(Device.DeviceModel.Description).Append("; ");
|
||||
}
|
||||
|
||||
descriptionBuilder.Append(Device.DeviceProfile.Description).Append(";");
|
||||
|
||||
string description = descriptionBuilder.ToString().Trim();
|
||||
if (description.Length > 1024)
|
||||
description = description.Substring(0, 1024);
|
||||
|
||||
account.SetDescription(DomainController, description);
|
||||
return ADGroup.FromSearchResult(SearchResult);
|
||||
}
|
||||
public static void SetDescription(this ActiveDirectoryMachineAccount account, Device Device)
|
||||
public static IEnumerable<ADGroup> AsADGroups(this IEnumerable<ADSearchResult> SearchResults)
|
||||
{
|
||||
var domain = account.GetDomain();
|
||||
|
||||
using (var domainController = domain.RetrieveWritableDomainController())
|
||||
{
|
||||
account.SetDescription(domainController, Device);
|
||||
}
|
||||
return SearchResults.Select(sr => ADGroup.FromSearchResult(sr));
|
||||
}
|
||||
public static ADGroup AsADGroup(this ADDirectoryEntry DirectoryEntry)
|
||||
{
|
||||
return ADGroup.FromDirectoryEntry(DirectoryEntry);
|
||||
}
|
||||
public static IEnumerable<ADGroup> AsADGroups(this IEnumerable<ADDirectoryEntry> DirectoryEntries)
|
||||
{
|
||||
return DirectoryEntries.Select(de => ADGroup.FromDirectoryEntry(de));
|
||||
}
|
||||
|
||||
public static void DisableAccount(this ActiveDirectoryMachineAccount account, DomainController DomainController)
|
||||
// Organisational Units
|
||||
public static ADOrganisationalUnit AsADOrganisationalUnit(this ADSearchResult SearchResult)
|
||||
{
|
||||
if (account.IsCriticalSystemObject)
|
||||
throw new InvalidOperationException(string.Format("This account {0} is a Critical System Active Directory Object and Disco refuses to modify it", account.DistinguishedName));
|
||||
|
||||
using (DirectoryEntry deAccount = DomainController.RetrieveDirectoryEntry(account.DistinguishedName))
|
||||
{
|
||||
int accountControl = (int)deAccount.Properties["userAccountControl"][0];
|
||||
int updatedAccountControl = (accountControl | 2);
|
||||
if (accountControl != updatedAccountControl)
|
||||
{
|
||||
deAccount.Properties["userAccountControl"][0] = updatedAccountControl;
|
||||
deAccount.CommitChanges();
|
||||
}
|
||||
}
|
||||
return ADOrganisationalUnit.FromSearchResult(SearchResult);
|
||||
}
|
||||
public static void DisableAccount(this ActiveDirectoryMachineAccount account)
|
||||
public static IEnumerable<ADOrganisationalUnit> AsADOrganisationalUnit(this IEnumerable<ADSearchResult> SearchResults)
|
||||
{
|
||||
var domain = account.GetDomain();
|
||||
|
||||
using (var domainController = domain.RetrieveWritableDomainController())
|
||||
{
|
||||
account.DisableAccount(domainController);
|
||||
}
|
||||
}
|
||||
public static void EnableAccount(this ActiveDirectoryMachineAccount account, DomainController DomainController)
|
||||
{
|
||||
if (account.IsCriticalSystemObject)
|
||||
throw new InvalidOperationException(string.Format("This account {0} is a Critical System Active Directory Object and Disco refuses to modify it", account.DistinguishedName));
|
||||
|
||||
using (DirectoryEntry deAccount = DomainController.RetrieveDirectoryEntry(account.DistinguishedName))
|
||||
{
|
||||
int accountControl = (int)deAccount.Properties["userAccountControl"][0];
|
||||
if ((accountControl & 2) == 2)
|
||||
{
|
||||
int updatedAccountControl = (accountControl ^ 2);
|
||||
deAccount.Properties["userAccountControl"][0] = updatedAccountControl;
|
||||
deAccount.CommitChanges();
|
||||
}
|
||||
}
|
||||
}
|
||||
public static void EnableAccount(this ActiveDirectoryMachineAccount account)
|
||||
{
|
||||
var domain = account.GetDomain();
|
||||
|
||||
using (var domainController = domain.RetrieveWritableDomainController())
|
||||
{
|
||||
account.EnableAccount(domainController);
|
||||
}
|
||||
}
|
||||
|
||||
public static bool UpdateNetbootGUID(this ActiveDirectoryMachineAccount account, DomainController DomainController, string UUID, string MACAddress)
|
||||
{
|
||||
if (account.IsCriticalSystemObject)
|
||||
throw new InvalidOperationException(string.Format("This account {0} is a Critical System Active Directory Object and Disco refuses to modify it", account.DistinguishedName));
|
||||
|
||||
Guid netbootGUID = Guid.Empty;
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(UUID))
|
||||
{
|
||||
netbootGUID = ActiveDirectoryExtensions.NetbootGUIDFromUUID(UUID);
|
||||
}
|
||||
else if (!string.IsNullOrWhiteSpace(MACAddress))
|
||||
{
|
||||
netbootGUID = ActiveDirectoryExtensions.NetbootGUIDFromMACAddress(MACAddress);
|
||||
}
|
||||
|
||||
if (netbootGUID != System.Guid.Empty && netbootGUID != account.NetbootGUID)
|
||||
{
|
||||
account.SetNetbootGUID(DomainController, netbootGUID);
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
public static bool UpdateNetbootGUID(this ActiveDirectoryMachineAccount account, string UUID, string MACAddress)
|
||||
{
|
||||
var domain = account.GetDomain();
|
||||
|
||||
using (var domainController = domain.RetrieveWritableDomainController())
|
||||
{
|
||||
return account.UpdateNetbootGUID(domainController, UUID, MACAddress);
|
||||
}
|
||||
}
|
||||
public static System.Guid NetbootGUIDFromMACAddress(string MACAddress)
|
||||
{
|
||||
string strippedMACAddress = MACAddress.Trim().Replace(":", string.Empty).Replace("-", string.Empty);
|
||||
bool flag = strippedMACAddress.Length == 12;
|
||||
System.Guid NetbootGUIDFromMACAddress;
|
||||
if (flag)
|
||||
{
|
||||
System.Guid guid = new System.Guid(string.Format("00000000-0000-0000-0000-{0}", strippedMACAddress));
|
||||
NetbootGUIDFromMACAddress = guid;
|
||||
}
|
||||
else
|
||||
{
|
||||
NetbootGUIDFromMACAddress = System.Guid.Empty;
|
||||
}
|
||||
return NetbootGUIDFromMACAddress;
|
||||
}
|
||||
public static System.Guid NetbootGUIDFromUUID(string UUID)
|
||||
{
|
||||
System.Guid result = new System.Guid(UUID);
|
||||
return result;
|
||||
}
|
||||
|
||||
public static object GetPropertyValue(this ActiveDirectoryMachineAccount account, string PropertyName, int Index = 0)
|
||||
{
|
||||
switch (PropertyName.ToLower())
|
||||
{
|
||||
case "name":
|
||||
return account.Name;
|
||||
case "samaccountname":
|
||||
return account.SamAccountName;
|
||||
case "distinguishedname":
|
||||
return account.DistinguishedName;
|
||||
case "objectsid":
|
||||
return account.SecurityIdentifier;
|
||||
case "netbootguid":
|
||||
return account.NetbootGUID;
|
||||
default:
|
||||
object[] adProperty;
|
||||
if (account.LoadedProperties.TryGetValue(PropertyName, out adProperty) && Index <= adProperty.Length)
|
||||
return adProperty[Index];
|
||||
else
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public static IPStatus PingComputer(this ActiveDirectoryMachineAccount account, int Timeout = 2000)
|
||||
{
|
||||
using (var p = new Ping())
|
||||
{
|
||||
PingReply reply = p.Send(account.DnsName, Timeout);
|
||||
return reply.Status;
|
||||
}
|
||||
}
|
||||
|
||||
public static void MoveOrganisationalUnit(this ActiveDirectoryMachineAccount account, DomainController DomainController, string NewOrganisationUnit)
|
||||
{
|
||||
if (account.IsCriticalSystemObject)
|
||||
throw new InvalidOperationException(string.Format("This account {0} is a Critical System Active Directory Object and Disco refuses to modify it", account.DistinguishedName));
|
||||
|
||||
var parentDistinguishedName = account.ParentDistinguishedName();
|
||||
|
||||
if (parentDistinguishedName != null && !parentDistinguishedName.Equals(NewOrganisationUnit, StringComparison.InvariantCultureIgnoreCase))
|
||||
{
|
||||
var domain = account.GetDomain();
|
||||
|
||||
// If no OU provided, place in default Computers container
|
||||
if (string.IsNullOrWhiteSpace(NewOrganisationUnit))
|
||||
NewOrganisationUnit = domain.GetDefaultComputerContainer();
|
||||
|
||||
if (!NewOrganisationUnit.EndsWith(domain.DistinguishedName, StringComparison.InvariantCultureIgnoreCase))
|
||||
throw new InvalidOperationException(string.Format("Unable to move AD Account from one domain [{0}] to another [{1}].", account.DistinguishedName, NewOrganisationUnit));
|
||||
|
||||
using (DirectoryEntry ou = DomainController.RetrieveDirectoryEntry(NewOrganisationUnit))
|
||||
{
|
||||
using (DirectoryEntry i = DomainController.RetrieveDirectoryEntry(account.DistinguishedName))
|
||||
{
|
||||
i.UsePropertyCache = false;
|
||||
i.MoveTo(ou);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static string ParentDistinguishedName(this ActiveDirectoryMachineAccount account)
|
||||
{
|
||||
// Determine Parent
|
||||
if (!string.IsNullOrWhiteSpace(account.DistinguishedName))
|
||||
return account.DistinguishedName.Substring(account.DistinguishedName.IndexOf(",") + 1);
|
||||
else
|
||||
return null;
|
||||
}
|
||||
|
||||
public static ActiveDirectoryDomain GetDomain(this ActiveDirectoryMachineAccount account)
|
||||
{
|
||||
var domain = ActiveDirectory.GetDomainByNetBiosName(account.Domain);
|
||||
|
||||
if (domain == null)
|
||||
throw new InvalidOperationException(string.Format("Unable to find Domain [{0}] for account [{1}]", account.Domain, account.Name));
|
||||
else
|
||||
return domain;
|
||||
return SearchResults.Select(sr => ADOrganisationalUnit.FromSearchResult(sr));
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user