683 lines
30 KiB
C#
683 lines
30 KiB
C#
using Disco.Data.Repository;
|
|
using Disco.Models.Interop.ActiveDirectory;
|
|
using Disco.Services.Tasks;
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.Diagnostics;
|
|
using System.DirectoryServices;
|
|
using System.DirectoryServices.ActiveDirectory;
|
|
using System.Linq;
|
|
using System.Security.Principal;
|
|
using System.Text;
|
|
using System.Threading.Tasks;
|
|
|
|
namespace Disco.Services.Interop.ActiveDirectory.Internal
|
|
{
|
|
internal static class ADInterop
|
|
{
|
|
public static List<ActiveDirectoryDomain> Domains { get; private set; }
|
|
public static ActiveDirectoryDomain PrimaryDomain { get; private set; }
|
|
public static ActiveDirectorySite Site { get; private set; }
|
|
internal static List<string> _ForestServers { get; set; }
|
|
private static bool _SearchEntireForest { get; set; }
|
|
private static bool _Initialized = false;
|
|
private static object _InitializeLock = new object();
|
|
|
|
#region Initialization
|
|
|
|
public static void Initialize(DiscoDataContext Database)
|
|
{
|
|
if (!_Initialized)
|
|
{
|
|
lock (_InitializeLock)
|
|
{
|
|
if (!_Initialized)
|
|
{
|
|
_SearchEntireForest = Database.DiscoConfiguration.ActiveDirectory.SearchEntireForest ?? true; // Default True
|
|
|
|
using (var computerDomain = Domain.GetComputerDomain())
|
|
{
|
|
PrimaryDomain = GetDomainInternal(computerDomain, Database);
|
|
|
|
Domains = computerDomain.Forest.Domains.Cast<Domain>().Select(d =>
|
|
{
|
|
if (d.Name == PrimaryDomain.DnsName)
|
|
return PrimaryDomain;
|
|
else
|
|
return GetDomainInternal(d, Database);
|
|
}).ToList();
|
|
}
|
|
|
|
Site = ActiveDirectorySite.GetComputerSite();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
private static ActiveDirectoryDomain GetDomainInternal(Domain d, DiscoDataContext Database)
|
|
{
|
|
string ldapPath = string.Format("LDAP://{0}/", d.Name);
|
|
string defaultNamingContext;
|
|
string configurationNamingContext;
|
|
string netBiosName;
|
|
|
|
using (var adRootDSE = new DirectoryEntry(ldapPath + "RootDSE"))
|
|
{
|
|
defaultNamingContext = adRootDSE.Properties["defaultNamingContext"][0].ToString();
|
|
configurationNamingContext = adRootDSE.Properties["configurationNamingContext"][0].ToString();
|
|
}
|
|
|
|
using (var configSearchRoot = new DirectoryEntry(ldapPath + "CN=Partitions," + configurationNamingContext))
|
|
{
|
|
var configSearchFilter = string.Format("(&(objectcategory=Crossref)(dnsRoot={0})(netBIOSName=*))", d.Name);
|
|
var configSearchLoadProperites = new string[] { "NetBIOSName" };
|
|
|
|
using (var configSearcher = new DirectorySearcher(configSearchRoot, configSearchFilter, configSearchLoadProperites, System.DirectoryServices.SearchScope.OneLevel))
|
|
{
|
|
SearchResult configResult = configSearcher.FindOne();
|
|
|
|
if (configResult != null)
|
|
netBiosName = configResult.Properties["NetBIOSName"][0].ToString();
|
|
else
|
|
netBiosName = null;
|
|
}
|
|
}
|
|
|
|
// Search Containers
|
|
var searchContainersAll = Database.DiscoConfiguration.ActiveDirectory.SearchContainers;
|
|
List<string> searchContainers = null;
|
|
|
|
if (searchContainersAll == null || searchContainersAll.Count == 0 || !searchContainersAll.TryGetValue(d.Name.ToLower(), out searchContainers))
|
|
searchContainers = new List<string>() { defaultNamingContext }; // No search constraints set - search entire tree
|
|
|
|
return new ActiveDirectoryDomain(d.Name, netBiosName, defaultNamingContext, searchContainers);
|
|
}
|
|
|
|
public static void UpdateSearchContainers(DiscoDataContext Database, IEnumerable<string> Containers)
|
|
{
|
|
Dictionary<string, List<string>> searchContainers = null;
|
|
|
|
if (Containers != null)
|
|
{
|
|
searchContainers = Containers
|
|
.Where(c => !string.IsNullOrWhiteSpace(c))
|
|
.Distinct()
|
|
.Select(c =>
|
|
{
|
|
ActiveDirectoryDomain d;
|
|
if (TryGetDomainByDistinguishedName(c, out d))
|
|
return Tuple.Create(d, c);
|
|
else
|
|
return null;
|
|
}).Where(i => i != null)
|
|
.GroupBy(i => i.Item1)
|
|
.ToDictionary(g => g.Key.DnsName.ToLower(), g => g.Select(i => i.Item2).ToList());
|
|
}
|
|
|
|
if (searchContainers == null || searchContainers.Count == 0)
|
|
{
|
|
Database.DiscoConfiguration.ActiveDirectory.SearchContainers = null;
|
|
|
|
// No search constraints set - search entire tree
|
|
Domains.ForEach(d => d.UpdateSearchContainers(new string[] { d.DistinguishedName }));
|
|
}
|
|
else
|
|
{
|
|
Database.DiscoConfiguration.ActiveDirectory.SearchContainers = searchContainers;
|
|
|
|
Domains.ForEach(d =>
|
|
{
|
|
List<string> domainContainers;
|
|
if (searchContainers.TryGetValue(d.DnsName.ToLower(), out domainContainers))
|
|
d.UpdateSearchContainers(domainContainers);
|
|
else
|
|
d.UpdateSearchContainers(Enumerable.Empty<string>());
|
|
});
|
|
}
|
|
}
|
|
|
|
public static bool UpdateSearchEntireForest(DiscoDataContext Database, bool SearchEntireForest)
|
|
{
|
|
if (SearchEntireForest == false)
|
|
{
|
|
Database.DiscoConfiguration.ActiveDirectory.SearchEntireForest = false;
|
|
ADInterop._SearchEntireForest = false;
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
var forestServers = LoadForestServers();
|
|
if (forestServers.Count <= ActiveDirectory.MaxForestServerSearch)
|
|
{
|
|
Database.DiscoConfiguration.ActiveDirectory.SearchEntireForest = true;
|
|
ADInterop._SearchEntireForest = true;
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
Database.DiscoConfiguration.ActiveDirectory.SearchEntireForest = false;
|
|
ADInterop._SearchEntireForest = false;
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Domain Getters
|
|
|
|
public static bool TryGetDomainByDistinguishedName(string DistinguishedName, out ActiveDirectoryDomain Domain)
|
|
{
|
|
// Find closest match
|
|
Domain = ADInterop.Domains.Where(d => DistinguishedName.EndsWith(d.DistinguishedName, StringComparison.InvariantCultureIgnoreCase))
|
|
.OrderByDescending(d => d.DistinguishedName.Length).FirstOrDefault();
|
|
|
|
return (Domain != null);
|
|
}
|
|
public static ActiveDirectoryDomain GetDomainByDistinguishedName(string DistinguishedName)
|
|
{
|
|
ActiveDirectoryDomain domain;
|
|
if (!TryGetDomainByDistinguishedName(DistinguishedName, out domain))
|
|
throw new ArgumentException(string.Format("The domain is unknown distinguished name: [{0}]", DistinguishedName), "Id");
|
|
return domain;
|
|
}
|
|
|
|
public static bool TryGetDomainByNetBiosName(string NetBiosName, out ActiveDirectoryDomain Domain)
|
|
{
|
|
Domain = ADInterop.Domains.FirstOrDefault(d => d.NetBiosName.Equals(NetBiosName, StringComparison.InvariantCultureIgnoreCase));
|
|
return (Domain != null);
|
|
}
|
|
public static ActiveDirectoryDomain GetDomainByNetBiosName(string NetBiosName)
|
|
{
|
|
ActiveDirectoryDomain domain;
|
|
if (!TryGetDomainByNetBiosName(NetBiosName, out domain))
|
|
throw new ArgumentException(string.Format("The specified domain is unknown [{0}]", NetBiosName), "Id");
|
|
return domain;
|
|
}
|
|
|
|
public static bool TryGetDomainByDnsName(string DnsName, out ActiveDirectoryDomain Domain)
|
|
{
|
|
Domain = ADInterop.Domains.FirstOrDefault(d => d.DnsName.Equals(DnsName, StringComparison.InvariantCultureIgnoreCase));
|
|
return (Domain != null);
|
|
}
|
|
public static ActiveDirectoryDomain GetDomainByDnsName(string DnsName)
|
|
{
|
|
ActiveDirectoryDomain domain;
|
|
if (!TryGetDomainByDnsName(DnsName, out domain))
|
|
throw new ArgumentException(string.Format("The specified domain is unknown [{0}]", DnsName), "Id");
|
|
return domain;
|
|
}
|
|
|
|
public static bool TryGetDomainFromId(string Id, out ActiveDirectoryDomain Domain)
|
|
{
|
|
if (string.IsNullOrWhiteSpace(Id))
|
|
throw new ArgumentNullException("Id");
|
|
|
|
var idSplit = UserExtensions.SplitUserId(Id);
|
|
|
|
if (idSplit.Item1 == null)
|
|
throw new ArgumentException(string.Format("The Id must include the Domain [{0}]", Id), "Id");
|
|
|
|
if (string.IsNullOrWhiteSpace(idSplit.Item1))
|
|
throw new ArgumentException(string.Format("The Id Domain was empty [{0}]", Id), "Id");
|
|
if (string.IsNullOrWhiteSpace(idSplit.Item2))
|
|
throw new ArgumentException(string.Format("The Id Name was empty [{0}]", Id), "Id");
|
|
|
|
return TryGetDomainByNetBiosName(idSplit.Item1, out Domain);
|
|
}
|
|
public static ActiveDirectoryDomain GetDomainFromId(string Id)
|
|
{
|
|
if (string.IsNullOrWhiteSpace(Id))
|
|
throw new ArgumentNullException("Id");
|
|
|
|
var idSplit = UserExtensions.SplitUserId(Id);
|
|
|
|
if (idSplit.Item1 == null)
|
|
throw new ArgumentException(string.Format("The Id must include the Domain [{0}]", Id), "Id");
|
|
|
|
if (string.IsNullOrWhiteSpace(idSplit.Item1))
|
|
throw new ArgumentException(string.Format("The Id Domain was empty [{0}]", Id), "Id");
|
|
if (string.IsNullOrWhiteSpace(idSplit.Item2))
|
|
throw new ArgumentException(string.Format("The Id Name was empty [{0}]", Id), "Id");
|
|
|
|
return GetDomainByNetBiosName(idSplit.Item1);
|
|
}
|
|
|
|
public static List<string> LoadForestServers()
|
|
{
|
|
if (_ForestServers == null)
|
|
{
|
|
lock (_InitializeLock)
|
|
{
|
|
if (_ForestServers == null)
|
|
{
|
|
var status = ADDiscoverForestServers.ScheduleNow();
|
|
status.CompletionTask.Wait();
|
|
}
|
|
}
|
|
}
|
|
return _ForestServers;
|
|
}
|
|
public static Task<List<string>> LoadForestServersAsync()
|
|
{
|
|
if (_ForestServers != null)
|
|
return Task.FromResult(_ForestServers);
|
|
|
|
lock (_InitializeLock)
|
|
{
|
|
if (_ForestServers != null)
|
|
return Task.FromResult(_ForestServers);
|
|
|
|
// Invoke Scheduled Task
|
|
var status = ADDiscoverForestServers.ScheduleNow();
|
|
|
|
return status.CompletionTask.ContinueWith(t =>
|
|
{
|
|
return ADInterop._ForestServers.ToList();
|
|
});
|
|
}
|
|
}
|
|
|
|
public static bool SearchEntireForest
|
|
{
|
|
get
|
|
{
|
|
if (_ForestServers != null && _ForestServers.Count > ActiveDirectory.MaxForestServerSearch)
|
|
return false; // Never
|
|
|
|
return _SearchEntireForest;
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Searching
|
|
|
|
public static IEnumerable<ActiveDirectorySearchResult> SearchAll(string LdapFilter, string[] LoadProperties)
|
|
{
|
|
return SearchAll(Domains, LdapFilter, LoadProperties);
|
|
}
|
|
public static IEnumerable<ActiveDirectorySearchResult> SearchAll(string LdapFilter, int ResultLimit, string[] LoadProperties)
|
|
{
|
|
return SearchAll(Domains, LdapFilter, ResultLimit, LoadProperties);
|
|
}
|
|
public static IEnumerable<ActiveDirectorySearchResult> SearchAll(IEnumerable<Tuple<ActiveDirectoryDomain, DomainController>> DomainsWithController, string LdapFilter, string[] LoadProperties)
|
|
{
|
|
return SearchAll(DomainsWithController, LdapFilter, null, LoadProperties);
|
|
}
|
|
public static IEnumerable<ActiveDirectorySearchResult> SearchAll(IEnumerable<ActiveDirectoryDomain> Domains, string LdapFilter, string[] LoadProperties)
|
|
{
|
|
return SearchAll(Domains, LdapFilter, null, LoadProperties);
|
|
}
|
|
public static IEnumerable<ActiveDirectorySearchResult> SearchAll(ActiveDirectoryDomain Domain, DomainController DomainController, string LdapFilter, string[] LoadProperties)
|
|
{
|
|
return SearchAll(Domain, DomainController, LdapFilter, null, LoadProperties);
|
|
}
|
|
public static IEnumerable<ActiveDirectorySearchResult> SearchAll(ActiveDirectoryDomain Domain, string LdapFilter, string[] LoadProperties)
|
|
{
|
|
return SearchAll(Domain, LdapFilter, null, LoadProperties);
|
|
}
|
|
public static IEnumerable<ActiveDirectorySearchResult> SearchAll(IEnumerable<ActiveDirectoryDomain> Domains, string LdapFilter, int? ResultLimit, string[] LoadProperties)
|
|
{
|
|
if (Domains == null || Domains.Count() == 0)
|
|
return Enumerable.Empty<ActiveDirectorySearchResult>();
|
|
|
|
var queries = Domains.Select(d => Tuple.Create(d, (DomainController)null)).ToList();
|
|
|
|
return SearchAll(queries, LdapFilter, ResultLimit, LoadProperties);
|
|
}
|
|
public static IEnumerable<ActiveDirectorySearchResult> SearchAll(IEnumerable<Tuple<ActiveDirectoryDomain, DomainController>> DomainsWithController, string LdapFilter, int? ResultLimit, string[] LoadProperties)
|
|
{
|
|
var queries = DomainsWithController.ToList();
|
|
|
|
IEnumerable<ActiveDirectorySearchResult> results;
|
|
|
|
switch (queries.Count)
|
|
{
|
|
case 0:
|
|
results = Enumerable.Empty<ActiveDirectorySearchResult>();
|
|
break;
|
|
case 1:
|
|
var singleQuery = queries.First();
|
|
results = SearchDomain(singleQuery.Item1, singleQuery.Item2, null, LdapFilter, ResultLimit, LoadProperties);
|
|
break;
|
|
default:
|
|
var taskFactory = new TaskFactory<IEnumerable<ActiveDirectorySearchResult>>();
|
|
var tasks = queries
|
|
.Select(query =>
|
|
taskFactory.StartNew(() =>
|
|
SearchDomain(query.Item1, query.Item2, null, LdapFilter, ResultLimit, LoadProperties))
|
|
).ToArray();
|
|
Task.WaitAll(tasks);
|
|
results = tasks.SelectMany(t => t.Result);
|
|
break;
|
|
}
|
|
|
|
if (ResultLimit.HasValue)
|
|
results = results.Take(ResultLimit.Value);
|
|
|
|
return results.ToList();
|
|
}
|
|
public static IEnumerable<ActiveDirectorySearchResult> SearchAll(ActiveDirectoryDomain Domain, string LdapFilter, int? ResultLimit, string[] LoadProperties)
|
|
{
|
|
return SearchAll(Domain, null, LdapFilter, ResultLimit, LoadProperties);
|
|
}
|
|
public static IEnumerable<ActiveDirectorySearchResult> SearchAll(ActiveDirectoryDomain Domain, DomainController DomainController, string LdapFilter, int? ResultLimit, string[] LoadProperties)
|
|
{
|
|
return SearchDomain(Domain, DomainController, null, LdapFilter, ResultLimit, LoadProperties);
|
|
}
|
|
|
|
public static IEnumerable<ActiveDirectorySearchResult> SearchScope(string LdapFilter, string[] LoadProperties)
|
|
{
|
|
return SearchScope(Domains, LdapFilter, LoadProperties);
|
|
}
|
|
public static IEnumerable<ActiveDirectorySearchResult> SearchScope(string LdapFilter, int ResultLimit, string[] LoadProperties)
|
|
{
|
|
return SearchScope(Domains, LdapFilter, ResultLimit, LoadProperties);
|
|
}
|
|
public static IEnumerable<ActiveDirectorySearchResult> SearchScope(IEnumerable<ActiveDirectoryDomain> Domains, string LdapFilter, string[] LoadProperties)
|
|
{
|
|
return SearchScope(Domains, LdapFilter, null, LoadProperties);
|
|
}
|
|
public static IEnumerable<ActiveDirectorySearchResult> SearchScope(IEnumerable<Tuple<ActiveDirectoryDomain, DomainController>> DomainsWithController, string LdapFilter, string[] LoadProperties)
|
|
{
|
|
return SearchScope(DomainsWithController, LdapFilter, null, LoadProperties);
|
|
}
|
|
public static IEnumerable<ActiveDirectorySearchResult> SearchScope(ActiveDirectoryDomain Domain, string LdapFilter, string[] LoadProperties)
|
|
{
|
|
return SearchScope(Domain, LdapFilter, null, LoadProperties);
|
|
}
|
|
public static IEnumerable<ActiveDirectorySearchResult> SearchScope(ActiveDirectoryDomain Domain, DomainController DomainController, string LdapFilter, string[] LoadProperties)
|
|
{
|
|
return SearchScope(Domain, DomainController, LdapFilter, null, LoadProperties);
|
|
}
|
|
public static IEnumerable<ActiveDirectorySearchResult> SearchScope(IEnumerable<ActiveDirectoryDomain> Domains, string LdapFilter, int? ResultLimit, string[] LoadProperties)
|
|
{
|
|
return SearchScope(Domains.Select(d => Tuple.Create(d, (DomainController)null)), LdapFilter, ResultLimit, LoadProperties);
|
|
}
|
|
public static IEnumerable<ActiveDirectorySearchResult> SearchScope(ActiveDirectoryDomain Domain, string LdapFilter, int? ResultLimit, string[] LoadProperties)
|
|
{
|
|
return SearchScope(Domain, null, LdapFilter, ResultLimit, LoadProperties);
|
|
}
|
|
public static IEnumerable<ActiveDirectorySearchResult> SearchScope(ActiveDirectoryDomain Domain, DomainController DomainController, string LdapFilter, int? ResultLimit, string[] LoadProperties)
|
|
{
|
|
if (Domain.SearchContainers == null || Domain.SearchContainers.Count == 0)
|
|
return Enumerable.Empty<ActiveDirectorySearchResult>();
|
|
|
|
var query = new List<Tuple<ActiveDirectoryDomain, DomainController>>() {
|
|
Tuple.Create(Domain, DomainController)
|
|
};
|
|
|
|
return SearchScope(query, LdapFilter, ResultLimit, LoadProperties);
|
|
}
|
|
public static IEnumerable<ActiveDirectorySearchResult> SearchScope(IEnumerable<Tuple<ActiveDirectoryDomain, DomainController>> DomainsWithController, string LdapFilter, int? ResultLimit, string[] LoadProperties)
|
|
{
|
|
var queries = DomainsWithController.SelectMany(d => d.Item1.SearchContainers, (d, sc) => Tuple.Create(d.Item1, d.Item2, sc)).ToList();
|
|
|
|
IEnumerable<ActiveDirectorySearchResult> results;
|
|
|
|
switch (queries.Count)
|
|
{
|
|
case 0:
|
|
results = Enumerable.Empty<ActiveDirectorySearchResult>();
|
|
break;
|
|
case 1:
|
|
var singleQuery = queries.First();
|
|
results = SearchDomain(singleQuery.Item1, singleQuery.Item2, singleQuery.Item3, LdapFilter, ResultLimit, LoadProperties);
|
|
break;
|
|
default:
|
|
var taskFactory = new TaskFactory<IEnumerable<ActiveDirectorySearchResult>>();
|
|
var tasks = queries
|
|
.Select(query =>
|
|
taskFactory.StartNew(() =>
|
|
SearchDomain(query.Item1, query.Item2, query.Item3, LdapFilter, ResultLimit, LoadProperties).ToList())
|
|
).ToArray();
|
|
Task.WaitAll(tasks);
|
|
results = tasks.SelectMany(t => t.Result);
|
|
break;
|
|
}
|
|
|
|
if (ResultLimit.HasValue)
|
|
results = results.Take(ResultLimit.Value);
|
|
|
|
return results.ToList();
|
|
}
|
|
|
|
private static IEnumerable<ActiveDirectorySearchResult> SearchDomain(ActiveDirectoryDomain Domain, DomainController DomainController, string SearchRoot, string LdapFilter, int? ResultLimit, string[] LoadProperties)
|
|
{
|
|
string ldapServer;
|
|
if (DomainController != null)
|
|
{
|
|
ldapServer = DomainController.Name;
|
|
}
|
|
else
|
|
{
|
|
var domainDC = Site.RetrieveReachableDomainControllers(Domain).FirstOrDefault();
|
|
if (domainDC != null)
|
|
ldapServer = domainDC.Name;
|
|
else
|
|
ldapServer = Domain.DnsName;
|
|
}
|
|
string searchRoot = SearchRoot ?? Domain.DistinguishedName;
|
|
string ldapPath = string.Format(@"LDAP://{0}/{1}", ldapServer, searchRoot);
|
|
|
|
using (DirectoryEntry rootEntry = new DirectoryEntry(ldapPath))
|
|
{
|
|
using (DirectorySearcher searcher = new DirectorySearcher(rootEntry, LdapFilter, LoadProperties, System.DirectoryServices.SearchScope.Subtree))
|
|
{
|
|
searcher.PageSize = 500;
|
|
|
|
if (ResultLimit.HasValue)
|
|
searcher.SizeLimit = ResultLimit.Value;
|
|
return searcher.FindAll().Cast<SearchResult>().Select(result => new ActiveDirectorySearchResult()
|
|
{
|
|
Domain = Domain,
|
|
SearchRoot = searchRoot,
|
|
Result = result,
|
|
});
|
|
}
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
|
|
public static string OfflineDomainJoinProvision(ActiveDirectoryDomain Domain, DomainController DomainController, string ComputerSamAccountName, string OrganisationalUnit, out string DiagnosticInformation)
|
|
{
|
|
StringBuilder diagnosticInfo = new StringBuilder();
|
|
string DJoinResult = null;
|
|
|
|
if (!string.IsNullOrWhiteSpace(ComputerSamAccountName))
|
|
ComputerSamAccountName = ComputerSamAccountName.TrimEnd('$');
|
|
if (string.IsNullOrWhiteSpace(ComputerSamAccountName) || ComputerSamAccountName.Length > 24)
|
|
throw new System.ArgumentException("Invalid Computer Name; > 0 and <= 24", "ComputerName");
|
|
|
|
// Ensure Specified OU Exists
|
|
if (!string.IsNullOrEmpty(OrganisationalUnit))
|
|
{
|
|
try
|
|
{
|
|
using (var deOU = DomainController.RetrieveDirectoryEntry(OrganisationalUnit))
|
|
{
|
|
if (deOU == null)
|
|
throw new Exception(string.Format("OU's Directory Entry couldn't be found at [{0}]", OrganisationalUnit));
|
|
}
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
throw new ArgumentException(string.Format("An error occurred while trying to locate the specified OU: {0}", OrganisationalUnit), "OrganisationalUnit", ex);
|
|
}
|
|
}
|
|
|
|
string tempFileName = System.IO.Path.GetTempFileName();
|
|
string argumentOU = (!string.IsNullOrWhiteSpace(OrganisationalUnit)) ? string.Format(" /MACHINEOU \"{0}\"", OrganisationalUnit) : string.Empty;
|
|
string arguments = string.Format("/PROVISION /DOMAIN \"{0}\" /DCNAME \"{1}\" /MACHINE \"{2}\"{3} /REUSE /SAVEFILE \"{4}\"",
|
|
Domain.DnsName,
|
|
DomainController.Name,
|
|
ComputerSamAccountName,
|
|
argumentOU,
|
|
tempFileName
|
|
);
|
|
ProcessStartInfo commandStarter = new ProcessStartInfo("DJOIN.EXE", arguments)
|
|
{
|
|
CreateNoWindow = true,
|
|
ErrorDialog = false,
|
|
LoadUserProfile = false,
|
|
RedirectStandardOutput = true,
|
|
RedirectStandardError = true,
|
|
UseShellExecute = false
|
|
};
|
|
diagnosticInfo.AppendFormat("{0} {1}", "DJOIN.EXE", arguments);
|
|
diagnosticInfo.AppendLine();
|
|
|
|
string stdOutput;
|
|
string stdError;
|
|
using (Process commandProc = Process.Start(commandStarter))
|
|
{
|
|
commandProc.WaitForExit(20000);
|
|
stdOutput = commandProc.StandardOutput.ReadToEnd();
|
|
stdError = commandProc.StandardError.ReadToEnd();
|
|
}
|
|
if (!string.IsNullOrWhiteSpace(stdOutput))
|
|
diagnosticInfo.AppendLine(stdOutput);
|
|
if (!string.IsNullOrWhiteSpace(stdError))
|
|
diagnosticInfo.AppendLine(stdError);
|
|
|
|
if (System.IO.File.Exists(tempFileName))
|
|
{
|
|
DJoinResult = System.Convert.ToBase64String(System.IO.File.ReadAllBytes(tempFileName));
|
|
System.IO.File.Delete(tempFileName);
|
|
}
|
|
if (string.IsNullOrWhiteSpace(DJoinResult))
|
|
throw new System.InvalidOperationException(string.Format("Domain Join Unsuccessful{0}Error: {1}{0}Output: {2}", System.Environment.NewLine, stdError, stdOutput));
|
|
|
|
DiagnosticInformation = diagnosticInfo.ToString();
|
|
|
|
return DJoinResult;
|
|
}
|
|
|
|
public static DirectoryEntry RetrieveDirectoryEntry(string DistinguishedName, out ActiveDirectoryDomain Domain)
|
|
{
|
|
if (string.IsNullOrWhiteSpace(DistinguishedName))
|
|
throw new ArgumentNullException("DistinguishedName");
|
|
|
|
// Find Domain
|
|
var domain = GetDomainByDistinguishedName(DistinguishedName);
|
|
|
|
if (domain == null)
|
|
throw new ArgumentException(string.Format("Unknown domain for DistinguishedName: {0}", DistinguishedName), "DistinguishedName");
|
|
|
|
var domainDC = Site.RetrieveReachableDomainControllers(domain).FirstOrDefault();
|
|
var ldapServer = domainDC != null ? domainDC.Name : domain.DnsName;
|
|
|
|
Domain = domain;
|
|
|
|
return new DirectoryEntry(string.Format(@"LDAP://{0}/{1}", ldapServer, DistinguishedName));
|
|
}
|
|
|
|
public static DomainController RetrieveWritableDomainController(this ActiveDirectoryDomain domain)
|
|
{
|
|
var adContext = new DirectoryContext(DirectoryContextType.Domain, domain.DnsName);
|
|
using (var adDomain = Domain.GetDomain(adContext))
|
|
{
|
|
return adDomain.FindDomainController(LocatorOptions.WriteableRequired);
|
|
}
|
|
}
|
|
|
|
public static DirectoryEntry RetrieveDirectoryEntry(this DomainController domainController, string DistinguishedName)
|
|
{
|
|
return new DirectoryEntry(string.Format(@"LDAP://{0}/{1}", domainController.Name, DistinguishedName));
|
|
}
|
|
|
|
public static void DeleteObjectRecursive(this DirectoryEntry directoryEntry)
|
|
{
|
|
DeleteObjectRecursiveInternal(directoryEntry);
|
|
|
|
using (var deParent = directoryEntry.Parent)
|
|
{
|
|
deParent.Children.Remove(directoryEntry);
|
|
}
|
|
}
|
|
private static void DeleteObjectRecursiveInternal(DirectoryEntry directoryEntry)
|
|
{
|
|
List<DirectoryEntry> children = directoryEntry.Children.Cast<DirectoryEntry>().ToList();
|
|
|
|
foreach (var child in children)
|
|
{
|
|
DeleteObjectRecursive(child);
|
|
directoryEntry.Children.Remove(child);
|
|
child.Dispose();
|
|
}
|
|
}
|
|
|
|
#region Helpers
|
|
|
|
internal static string ConvertBytesToSDDLString(byte[] SID)
|
|
{
|
|
SecurityIdentifier sID = new SecurityIdentifier(SID, 0);
|
|
|
|
return sID.ToString();
|
|
}
|
|
|
|
internal static byte[] ConvertSDDLStringToBytes(string SidSsdlString)
|
|
{
|
|
SecurityIdentifier sID = new SecurityIdentifier(SidSsdlString);
|
|
|
|
var sidBytes = new byte[sID.BinaryLength];
|
|
|
|
sID.GetBinaryForm(sidBytes, 0);
|
|
|
|
return sidBytes;
|
|
}
|
|
|
|
internal static byte[] BuildPrimaryGroupSid(byte[] UserSID, int PrimaryGroupId)
|
|
{
|
|
var groupSid = (byte[])UserSID.Clone();
|
|
|
|
int ridOffset = groupSid.Length - 4;
|
|
int groupId = PrimaryGroupId;
|
|
for (int i = 0; i < 4; i++)
|
|
{
|
|
groupSid[ridOffset + i] = (byte)(groupId & 0xFF);
|
|
groupId >>= 8;
|
|
}
|
|
|
|
return groupSid;
|
|
}
|
|
|
|
internal static string ConvertBytesToBinarySidString(byte[] SID)
|
|
{
|
|
StringBuilder escapedSid = new StringBuilder();
|
|
|
|
foreach (var sidByte in SID)
|
|
{
|
|
escapedSid.Append('\\');
|
|
escapedSid.Append(sidByte.ToString("x2"));
|
|
}
|
|
|
|
return escapedSid.ToString();
|
|
}
|
|
|
|
internal static string EscapeLdapQuery(string query)
|
|
{
|
|
return query.Replace("*", "\\2a").Replace("(", "\\28").Replace(")", "\\29").Replace("\\", "\\5c").Replace("NUL", "\\00").Replace("/", "\\2f");
|
|
}
|
|
internal static string FormatGuidForLdapQuery(System.Guid g)
|
|
{
|
|
checked
|
|
{
|
|
System.Text.StringBuilder sb = new System.Text.StringBuilder();
|
|
byte[] array = g.ToByteArray();
|
|
for (int i = 0; i < array.Length; i++)
|
|
{
|
|
byte b = array[i];
|
|
sb.Append("\\");
|
|
sb.Append(b.ToString("X2"));
|
|
}
|
|
return sb.ToString();
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
}
|
|
}
|