In order to determine if computer accounts are orphaned, I would like to query all domain controllers of all trusted domains to retrieve the lastLogon and lastLogonTimeStamp for all computers. I have a program that works (at least in my test environment), but I have a few questions, that I hope you might be able to answer.
Are the methods I’m using to find the domains and domain controllers and then retrieve the AD information, using the least amount of resources (network / domain controller CPU and RAM) as possible? How could they be improved?
Is it possible to have more than 1 value in a dictionary key / value pair?
Having a dictionary for LastLogIn and a different one for LastLogInTimestamp seems a waste.
Referring to the dictionary and the AD properties: How can I check for non-existent values as opposed to using Try / Catch?
try
{ // Is this DC more current than the last?
if (dict_LastLogIn[pc] < (long)result.Properties["lastlogon"][0])
{
dict_LastLogIn[pc] = (long)result.Properties["lastlogon"][0];
}
}
catch
{ // The item doesn't exist yet..
try
{
dict_LastLogIn[pc] = (long)result.Properties["lastlogon"][0];
}
catch
{ // .. or
// There is no last LastLogin...
dict_LastLogIn[pc] = 0;
}
}
Here the entire code:
using System;
using System.Collections.Generic;
using System.DirectoryServices;
using System.DirectoryServices.ActiveDirectory;
namespace dictionary
{
class Program
{
internal static Dictionary<string, long> dict_LastLogIn =
new Dictionary<string, long>();
internal static Dictionary<string, long> dict_LastLogInTimeStamp =
new Dictionary<string, long>();
internal static Dictionary<string, DateTime> output =
new Dictionary<string, DateTime>();
internal static bool AreAllDCsResponding = true;
static void Main(string[] args)
{
Console.BufferWidth = 150;
Console.BufferHeight = 9999;
Console.WindowWidth = 150;
Dictionary<String, int> dict_domainList = new Dictionary<String, int>();
Dictionary<String, int> dict_dcList = new Dictionary<String, int>();
//Get the current domain's trusts.
Domain currentDomain = Domain.GetCurrentDomain();
Console.WriteLine("Retrieved the current Domain as {0}", currentDomain.ToString());
var domainTrusts = currentDomain.GetAllTrustRelationships();
Console.WriteLine(" {0} trusts were found.", domainTrusts.Count);
//Add the current domain to the dictonary. It won't be in domainTrusts!
dict_domainList.Add(currentDomain.ToString(), 0);
// Then add the other domains to the dictonary...
foreach (TrustRelationshipInformation trust in domainTrusts)
{
dict_domainList.Add(trust.TargetName.Substring(0, trust.TargetName.IndexOf(".")).ToUpper(), 0);
Console.WriteLine(" Adding {0} to the list of trusts.", trust.TargetName.Substring(0, trust.TargetName.IndexOf(".")).ToUpper());
}
// Now get all DCs per domain
foreach (var pair in dict_domainList)
{
DirectoryContext dc = new DirectoryContext(DirectoryContextType.Domain, pair.Key);
Domain _Domain = Domain.GetDomain(dc);
foreach (DomainController Server in _Domain.DomainControllers)
{
dict_dcList.Add(Server.Name, 0);
Console.WriteLine(" Adding {0} to the list of DCs.", Server.Name.ToUpper());
}
// Now search through every DC
foreach (var _pair in dict_dcList)
{
Console.WriteLine(" Querying {0} for Computer objects.", _pair.Key.ToUpper());
Search(pair.Key);
Console.WriteLine("\n");
Console.WriteLine("The following Computer objects were found:");
}
if (AreAllDCsResponding == true)
{
ConvertTimeStamp(dict_LastLogIn);
}
else
{
ConvertTimeStamp(dict_LastLogInTimeStamp);
}
Console.ReadLine();
}
}
internal static void Search(string domainName)
{
DirectoryEntry entry = new DirectoryEntry("LDAP://" + domainName);
DirectorySearcher mySearcher = new DirectorySearcher(entry);
mySearcher.Filter = ("(&(ObjectCategory=computer))");//(lastlogon=*)(lastlogonTimeStamp=*))");
mySearcher.SizeLimit = int.MaxValue;
mySearcher.PropertiesToLoad.Add("DistinguishedName");
mySearcher.PropertiesToLoad.Add("lastlogon");
mySearcher.PropertiesToLoad.Add("lastlogonTimeStamp");
try
{
foreach (System.DirectoryServices.SearchResult result in mySearcher.FindAll())
{
string pc = result.Properties["DistinguishedName"][0].ToString();
try
{ // Is this DC more current than the last?
if (dict_LastLogIn[pc] < (long)result.Properties["lastlogon"][0])
{
dict_LastLogIn[pc] = (long)result.Properties["lastlogon"][0];
}
}
catch
{ // The item doesn't exist yet..
try
{
dict_LastLogIn[pc] = (long)result.Properties["lastlogon"][0];
}
catch
{ // .. or
// There is no last LastLogin...
dict_LastLogIn[pc] = 0;
}
}
try
{
// Not yet replicated?...
if (dict_LastLogInTimeStamp[pc] < (long)result.Properties["lastlogonTimeStamp"][0])
{
dict_LastLogInTimeStamp[pc] = (long)result.Properties["lastlogonTimeStamp"][0];
}
}
catch
{ // The item doesn't exist yet..
try
{
dict_LastLogInTimeStamp[pc] = (long)result.Properties["lastlogonTimeStamp"][0];
}
catch
{ // .. or
// There is no last LastLoginTimeStamp...
dict_LastLogInTimeStamp[pc] = 0;
}
}
}
}
catch (System.Runtime.InteropServices.COMException)
{
//If even one DC doesn't answer, don't use LastLogon!
//Use the less accurate, but replicated(!) LastLogonTimeStamp.
AreAllDCsResponding = false;
}
}
internal static void ConvertTimeStamp(Dictionary<string, long> _dict)
{
foreach (var pair in _dict)
{
output.Add(pair.Key, DateTime.FromFileTime(pair.Value));
Console.WriteLine("{0} - {1}", pair.Key, DateTime.FromFileTime(pair.Value));
}
}
}
}
Thanks for any help you can offer.
At a high level, I'm not sure what your end-game is with this, but, are you sure you really want to query every single DC for lastLogon? This could be really expensive. Also, why are you walking trust chains? Are you sure you don't just want all the domains in a given Forest (Forest.Domains)?
In answer to your questions:
This looks OK perf-wise, however you should do a couple things:
Tweak your filter to (&(objectCategory=computer)(objectClass=computer))
Add mySearcher.PageSize = 1000
Remove mySearcher.SizeLimit = int.MaxValue
You could use a Tuple - http://msdn.microsoft.com/en-us/library/system.tuple(VS.90).aspx. Or just define a custom class as your value and then declare Dictionary as your dictionary:
public class LogonTimeStamps
{
public long LastLogon { get; set; }
public long LastLogonTimeStamp { get; set; }
}
For the Dictionary, use myDictionary.ContainsKey(yourKey). For AD, you should be able to use result.Properties.Contains("yourAttribute").
Related
Good day, I've been fiddling around with the Eventlog method, and with fiddling around I was able to count how many entry.replacementstrings[5] aka usernames would be there.
public int countUsers { get; set; }
public string User { get; set; }
public Users(int count, string name)
{
countUsers = count;
User = name;
}
public void getCountUsers()
{
number = 0; //
UserList = new ObservableCollection<Users>();
EventLog myNewLog = new EventLog();
myNewLog.Log = "Security";
foreach (EventLogEntry entry in myNewLog.Entries)
{
if (entry.InstanceId == 4624 && entry.TimeWritten.Date == DateTime.Today)
{
if (UserList.Count > 0)
{
bool check = false;
foreach (var user in UserList)
{
if (user.User == entry.ReplacementStrings[5])
{
user.countUsers += 1;
check = true;
}
}
if (!check)
{
Users u = new Users(1, entry.ReplacementStrings[5]);
UserList.Add(u);
}
}
else
{
Users u = new Users(1, entry.ReplacementStrings[5]);
UserList = new ObservableCollection<Users>();
UserList.Add(u);
}
}
}
}
public void counter()
{
var totalUsers = UserList.Sum(user => user.countUsers);
foreach (var user in UserList)
{
Console.WriteLine("There has been {0} users on {1}", user.countUsers, DateTime.Today.ToShortDateString());
}
}
Is what I currently have. What I now want to be able to do is, add a regex to the writeline so it doesn't count the user SYSTEM.
I was able to do it with but that would print out every individual user, but instead I want the general/global idea of how many people were online at said date.
So I need to know how to do it with getting rid of the for each loop, and just getting the user.countUsers.
foreach (var user in UserList)
{
Regex User = new Regex(#"SYSTEM");
Match match = User.Match(user.User);
if (!match.Success)
{}
}
I now don't know how to call the variable so my regex works. Anyone know how to fix it, maybe possibly without a regex?
(Side note: I also need help with the fact that the EventLog is 2x, when it should be 1 by the ones that are legit. I would need to see how I would filter that)
You don't need to use Regex since the string you want to exclude is literal, you can use Linq:
foreach (var user in UserList.Where(u => u.User != "SYSTEM"))
{
Console.WriteLine("There has been {0} users on {1}", user.countUsers, DateTime.Today.ToShortDateString());
}
Good Morning everyone,
Does anyone know on how to use MPXJ v5.1.5 to effectively to read the MPP. project file to get the Outline Code Values linked to their assigned tasks.
I already have found a way of getting the tasks and the time scale data for them but how do I find out what Outline Code or custom field is linked to any task? This will help to create reports on how these custom fields are going.
Here is my main piece of code used to retrieve the Tasks with their time scale data. This piece of code is running on a Background worker and report progress.
void Work_DoWork(object sender, DoWorkEventArgs e)
{
try
{
Document_Details_To_Open Document_Selected_Details = e.Argument as Document_Details_To_Open;
ProjectReader reader = ProjectReaderUtility.getProjectReader(Document_Selected_Details.FileName);
MPXJ.ProjectFile mpx = reader.read(Document_Selected_Details.FileName);
int count = mpx.AllTasks.Size();
int stepsize = 100002 / count;
int pos = 1;
foreach (MPXJ.Task task in mpx.AllTasks.ToIEnumerable())
{
Task_Type task_ = new Task_Type()
{
Name = task.Name,
Total_Days = task.Duration.toString(),
ID = task.ID.toString()
};
//Task.getFieldByAlias()
//can add task above to MVVM connection
foreach (MPXJ.ResourceAssignment Resource in task.ResourceAssignments.ToIEnumerable())//this will only run once per task , I use the ResourceAssignment variable to get the duration data
{
//use the selected document details given
Dictionary<string, java.util.List> worklist = new Dictionary<string, java.util.List>();
foreach (string Work_type in Document_Selected_Details.Data_To_Import)
{
worklist.Add(Work_type, Get_Some_work(Resource, Work_type));
}
int Length_of_data_to_retrieve = Get_Time_Scale_int(Document_Selected_Details.Time_Scale_Units, task.Duration.Duration);
TimescaleUtility TimeScale = new TimescaleUtility();
java.util.ArrayList datelist = TimeScale.CreateTimescale(task.Start, Get_Scale_Type(Document_Selected_Details.Time_Scale_Units), Length_of_data_to_retrieve);
MPXJ.ProjectCalendar calendar = Resource.Calendar;
TimephasedUtility utility = new TimephasedUtility();
Dictionary<string, java.util.ArrayList> durationlist = new Dictionary<string, java.util.ArrayList>();
foreach (KeyValuePair<string, java.util.List> item in worklist)
{
java.util.ArrayList duration = utility.SegmentWork(calendar, item.Value, Get_Scale_Type(Document_Selected_Details.Time_Scale_Units), datelist);
durationlist.Add(item.Key, duration);
}
Dictionary<string, List<string>> ssss = new Dictionary<string, List<string>>();
foreach (var s in durationlist)
{
string key = s.Key;
List<string> Hours = new List<string>();
foreach (var hours in s.Value.toArray().ToList())
{
Hours.Add(hours.ToString());
}
ssss.Add(key, Hours);
}
Task_With_All all = new Models.Task_With_All()
{
Task_Name = task.Name,
Time_Step_Type = Document_Selected_Details.Time_Scale_Units,
Duration_List = ssss,
StartDate = task.Start.ToDateTime().ToString(),
Total_duration = Length_of_data_to_retrieve.ToString()
};
Task_With_All_list.Add(all);
//I have now every task and their Time scale data but I still need to know if the tasks could have custom fields connected or not
}
pos += stepsize;
Work.ReportProgress(pos);
}
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}
Any help would be greatly appreciated.
Thanks to Jon Iles, the answer on how to get the Outline Codes for a Task became very simple. In MS Project there is a limit of 10 Outline Codes the users can assign to Tasks. To Get these Outline Codes that has been Assigned to a Task using MPXJ v5.1.5, you can use this to get them :
//this code comes from my code block in the question.
...
foreach (MPXJ.Task task in mpx.AllTasks.ToIEnumerable())
{
//if the string values retrieved from these has a valid value that's returned, that value is the Outline Code assigned to the task
string Outline_code_1 = task.GetOutlineCode(1);
string Outline_code_2 = task.GetOutlineCode(2);
string Outline_code_3 = task.GetOutlineCode(3);
string Outline_code_4 = task.GetOutlineCode(4);
string Outline_code_5 = task.GetOutlineCode(5);
string Outline_code_6 = task.GetOutlineCode(6);
string Outline_code_7 = task.GetOutlineCode(7);
string Outline_code_8 = task.GetOutlineCode(8);
string Outline_code_9 = task.GetOutlineCode(9);
string Outline_code_10 = task.GetOutlineCode(10);
}
...
I am trying to find a txt files over computers of a domain network.
What I have done till now:
I have the list of all computers of the domain in an array.So I am iterating each computer with its corresponding address with the help of getfile command.
Where I am stuck :
There are some computers over which I don't have access.So my search is either taking a long time to leap those exceptions or it gets struck at some point.As there are more that 500 systems so I want to increase the speed and accuracy of my program.
I am mostly getting network not found error.
Here is my code:
namespace ABC
{
class Program
{
static void Main(string[] args)
{
List<string> cnames=new List<string>();
DirectoryEntry entry = new DirectoryEntry("LDAP://abc.com", "username", "password", AuthenticationTypes.Secure);
DirectorySearcher mySearcher = new DirectorySearcher(entry);
mySearcher.Filter = ("(objectClass=computer)");
foreach (SearchResult resEnt in mySearcher.FindAll())
{
string name = resEnt.GetDirectoryEntry().Name;
if (name.StartsWith("CN="))
name = name.Remove(0, "CN=".Length);
cnames.Add(name);
}
int cnumbers=cnames.Count;
for (int i = 0; i < cnumbers;i++ )
{
string s = "\\\\" + cnames[i] + "\\ab\\cd";
string[] dirs = null;
Console.WriteLine("Name of Computer=" + cnames[i]);
try
{
dirs = Directory.GetFiles(s);
try
{
foreach (string dir in dirs)
{
Console.WriteLine(dir);
}
}
catch (Exception e)
{
}
}
catch (Exception)
{
}
}
}
}
}
I have a telephony application, in which I want to invoke simultaneous calls,. Each call will occupy a channel or port. So I added all channels to a BlockingCollection. The application is a windows service.
Let's see the code.
public static BlockingCollection<Tuple<ChannelResource, string>> bc = new BlockingCollection<Tuple<ChannelResource, string>>();
public static List<string> list = new List<string>();// then add 100 test items to it.
The main application has the code:
while (true)
{
ThreadEvent.WaitOne(waitingTime, false);
lock (SyncVar)
{
Console.WriteLine("Block begin");
for (int i = 0; i < ports; i++)
{
var firstItem = list.FirstOrDefault();
if (bc.Count >= ports)
bc.CompleteAdding();
else
{
ChannelResource cr = OvrTelephonyServer.GetChannel();
bc.TryAdd(Tuple.Create(cr, firstItem));
list.Remove(firstItem);
}
}
pc.SimultaneousCall();
Console.WriteLine("Blocking end");
if (ThreadState != State.Running) break;
}
Now for the simultaneous call code:
public void SimultaneousCall()
{
Console.WriteLine("There are {0} channels to be processed.", bc.Count);
var workItemBlock = new ActionBlock<Tuple<ChannelResource, string>>(
workItem =>
{
ProcessEachChannel(workItem);
});
foreach (var workItem in bc.GetConsumingEnumerable())
{
bool result = workItemBlock.SendAsync(workItem).Result;
}
workItemBlock.Complete();
}
private void ProcessEachChannel(Tuple<ChannelResource, string> workItem)
{
ChannelResource cr = workItem.Item1;
string sipuri = workItem.Item2;
VoiceResource vr = workItem.Item1.VoiceResource;
workItem.Item1.Disconnected += new Disconnected(workItemItem1_Disconnected);
bool success = false;
try
{
Console.WriteLine("Working on {0}", sipuri);
DialResult dr = new DialResult();
// blah blah for calling....
}
catch (Exception ex)
{
Console.WriteLine("Exception: {0}", ex.Message);
}
finally
{
if (cr != null && cr.VoiceResource != null)
{
cr.Disconnect();
cr.Dispose();
cr = null;
Console.WriteLine("Release channel for item {0}.", sipuri);
}
}
}
The question was when I tested the application with 4 ports, I thought the code should reach at
Console.WriteLine("Blocking end");
However it was not. Please see the snapshot.
The application is just hanging on after releasing the last channel. I guess that I may use the blockingcollection incorrectly. Thanks for help.
UPDATE:
Even I changed the code by using POST action as below, the situation is still unchanged.
private bool ProcessEachChannel(Tuple<ChannelResource, string> workItem)
{
// blah blah to return true or false respectively.
public void SimultaneousCall()
{
Console.WriteLine("There are {0} channels to be processed.", bc.Count);
var workItemBlock = new ActionBlock<Tuple<ChannelResource, string>>(
workItem =>
{
bool success = ProcessEachChannel(workItem);
});
foreach (var workItem in bc.GetConsumingEnumerable())
{
workItemBlock.Post(workItem);
}
workItemBlock.Complete();
}
I believe the problem is that you never call bc.CompleteAdding(): the if means it would be called in ports + 1-th iteration of the loop, but the loop iterates only ports-times. Because of this, GetConsumingEnumerable() returns a sequence that never ends, which means the foreach inside SimultaneousCall() blocks forever.
I think the right solution is to call bc.CompleteAdding() after the for loop, not in an impossible condition inside it.
First of all, please forgive me if I'm not using the correct terminologies. Correct me wherever I'm using the wrong terminology.
The objective is to programmatically retrieve the lastLogon date of a given username.
We have what I believe is a forest; two AD servers like - adserver01.aa.mycompany.com and adserver02.aa.mycompany.com
I connected to these servers from a third machine using Microsoft's ADExplorer to inspect the objects. There I see some users having lastLogon date available in adserver01, but not in adserver02. For example, the value for lastLogon is 0x0 in adserver02 whereas it is a valid date in adserver01 for some users.
The code I've developed so far as a Windows Forms application, works fine if only one AD Server is involved. How do I check both servers and return the non-zero value if available, in either for lastLogon date attribute?
private static string GetLastActivityDate(string UserName)
{
string domainAndUserName = String.Format(#"LDAP://aa.mycompany.com/CN={0},OU=CLIENT_PROD,OU=clients.mycompany.com,DC=aa,DC=mycompany,DC=com", UserName);
string OUAdminUserName = "abc";
string OUAdminPassword = "xyz";
AuthenticationTypes at = AuthenticationTypes.Secure;
DateTime lastActivityDate;
string returnvalue;
long lastLogonDateAsLong;
using (DirectoryEntry entryUser = new DirectoryEntry(domainAndUserName, OUAdminUserName, OUAdminPassword, at))
using (DirectorySearcher mysearcher = new DirectorySearcher(entryUser))
try
{
using (SearchResultCollection results = mysearcher.FindAll())
{
if (results.Count >= 1)
{
DirectoryEntry de = results[0].GetDirectoryEntry();
lastLogonDateAsLong = GetInt64(de, "lastLogon");
try
{
if (lastLogonDateAsLong != -1)
{
lastActivityDate = DateTime.FromFileTime(lastLogonDateAsLong);
returnvalue = lastActivityDate.ToString();
}
else
{
returnvalue = "-Not available-";
}
}
catch (System.ArgumentOutOfRangeException aore)
{
returnvalue = "Not available";
}
}
else
{
returnvalue = string.Empty;
}
}
}
catch (System.DirectoryServices.DirectoryServicesCOMException dsce)
{
returnvalue = "- Not available -";
}
return returnvalue;
}
Thank you.
EDIT:
private static Int64 GetInt64(DirectoryEntry entry, string attr)
{
DirectorySearcher ds = new DirectorySearcher(
entry,
String.Format("({0}=*)", attr),
new string[] { attr },
SearchScope.Base
);
SearchResult sr = ds.FindOne();
if (sr != null)
{
if (sr.Properties.Contains(attr))
{
return (Int64)sr.Properties[attr][0];
}
}
return -1;
}
Forgot to mention, the AD schema, structure etc, looks exactly alike in the two servers.
Check this post
http://www.codeproject.com/Articles/19181/Find-LastLogon-Across-All-Windows-Domain-Controlle
I had the same issue but but only for one domain,
I solved it by using the following code however i'm checking the lastLogin of all users
public static Dictionary<string, DateTime> UsersLastLogOnDate()
{
var lastLogins = new Dictionary<string, DateTime>();
DomainControllerCollection domains = Domain.GetCurrentDomain().DomainControllers;
foreach (DomainController controller in domains)
{
try
{
using (var directoryEntry = new DirectoryEntry(string.Format("LDAP://{0}", controller.Name)))
{
using (var searcher = new DirectorySearcher(directoryEntry))
{
searcher.PageSize = 1000;
searcher.Filter = "(&(objectClass=user)(!objectClass=computer))";
searcher.PropertiesToLoad.AddRange(new[] { "distinguishedName", "lastLogon" });
foreach (SearchResult searchResult in searcher.FindAll())
{
if (searchResult.Properties.Contains("lastLogon"))
{
var lastLogOn = DateTime.FromFileTime((long)searchResult.Properties["lastLogon"][0]);
var username = Parser.ParseLdapAttrValue(searchResult.Properties["distinguishedName"][0].ToString());
if (lastLogins.ContainsKey(username))
{
if (DateTime.Compare(lastLogOn, lastLogins[username]) > 0)
{
lastLogins[username] = lastLogOn;
}
}
else
{
lastLogins.Add(username, lastLogOn);
}
}
}
}
}
}
catch (System.Runtime.InteropServices.COMException comException)
{
// Domain controller is down or not responding
Log.DebugFormat("Domain controller {0} is not responding.",controller.Name);
Log.Error("Error in one of the domain controllers.", comException);
continue;
}
}
return lastLogins;
}
On top of the code you can use the following to get all domains in a forest.
Forest currentForest = Forest.GetCurrentForest();
DomainCollection domains = currentForest.Domains;
foreach(Domain domain in domains)
{
// check code above
}
There may be a simpler approach? There is actually another attribute lastLogonTimestamp, added with 2003 domain level I think, that tries to keep a single, consistent value across the domain for the last login. Alas, it has a bizarre replication time pattern, and could be up to two weeks out of date.