List of online AD users in domain network C# - c#

There is a domain network with are connected computers.
Need a code that will display a list of Active Directory users connected to it now (using them / connected remotely).
Something similar to the "Users" tab in Task Manager, only for a domain network.
I tried to do this with WMI.
Classes Win32_LogonSession, Win32_LoggedOnUser, Win32_Account
The principle is this:
From active Win32_LogonSession weeded out LogonType = 2, 10 и 11.
By LogonId from Win32_LoggedOnUser get name and domain, search it in Win32_Account (to weed out system sessions) (Below there will be a code, maybe someone will come in handy)
But I ran into a problem : the event Session_onEnd is triggered far from 100% of cases, and LogOff seems not to be recorded at all, or is recorded incorrectly.
As a result, I get a lot of "hanging" sessions that users connect to, or create new ones when they log in. Analysis of the Windows event log in this regard does not give anything useful either. (LogOff is still incorrect)
So, if I can connect to a computer and get all the information out of it, how to extract - is it in use now and who is using it?
using System;
using System.Collections.Generic;
using System.Data;
using System.DirectoryServices;
using System.DirectoryServices.ActiveDirectory;
using System.Linq;
using System.Management;
using System.Net;
using System.Text.RegularExpressions;
using System.Windows.Forms;
namespace UserManager
{
public partial class LogOnSession : Form
{
List<LogOnSessionR> SessionsList = new List<LogOnSessionR>();
List<LoggedOnUser> LoggedList = new List<LoggedOnUser>();
List<PC> CompList = new List<PC>();
public LogOnSession()
{
InitializeComponent();
}
private void LogOnSession_Load(object sender, EventArgs e)
{
if (Form1.SelfRef != null)
{
CBDomain.Items.Add("All Forest");
foreach (object a in Form1.SelfRef.CBDomain.Items)
{
CBDomain.Items.Add(a);
}
CBDomain.SelectedIndex = Form1.SelfRef.CBDomain.SelectedIndex;
}
GetComputersInDomain(CBDomain.SelectedIndex);
CBComputers.SelectedIndex = 0;
}
private void CBDomain_SelectedIndexChanged(object sender, EventArgs e)
{
LogOnListView.Items.Clear();
GetComputersInDomain(CBDomain.SelectedIndex);
}
public void GetComputersInDomain(int I)
{
CompList.Clear();
CBComputers.Items.Clear();
if (I == 0)
{
DirectoryEntry de = new DirectoryEntry();
DirectorySearcher myADSearcher = new DirectorySearcher(de);
myADSearcher.Filter = "(&(objectClass=computer))";
foreach (SearchResult sr in myADSearcher.FindAll())
{
PC pc = new PC();
pc.Name = removeCN(sr.GetDirectoryEntry().Name);
pc.IP = getPCIP(pc.Name);
CompList.Add(pc);
}
}
else
{
DirectoryEntry de = new DirectoryEntry("LDAP://" + Form1.ListDomain[I-1].Name);
DirectorySearcher myADSearcher = new DirectorySearcher(de);
myADSearcher.Filter = "(&(objectClass=computer))";
foreach (SearchResult sr in myADSearcher.FindAll())
{
PC pc = new PC();
pc.Name = removeCN(sr.GetDirectoryEntry().Name);
pc.IP = getPCIP(pc.Name);
CompList.Add(pc);
}
}
CBComputers.Items.Add("All");
foreach (PC pc in CompList)
{
CBComputers.Items.Add(pc.Name);
}
}
public string removeCN(string str)
{
if (str.Contains("CN="))
{
return str.Replace("CN=", "");
}
else
{
return str;
}
}
public string getPCIP(string hostName)
{
IPHostEntry hostEntry;
hostEntry = Dns.GetHostEntry(hostName);
if (hostEntry.AddressList.Length > 0)
{
return hostEntry.AddressList[0].ToString();
//you might get more than one ip for a hostname since
//DNS supports more than one record
}
return null;
}
private void RefreshL_Click(object sender, EventArgs e)
{
ViewResult(getInfo());
}
public void ViewResult(List<LogonUser> results)
{
LogOnListView.Items.Clear();
if (results.Count == 0) { return; }
int i = 0;
foreach (LogonUser result in results)
{
LogOnListView.Items.Add(i.ToString());
LogOnListView.Items[i].SubItems.Add(result.SamAccountName);
LogOnListView.Items[i].SubItems.Add(result.FullName);
LogOnListView.Items[i].SubItems.Add(result.Computer);
LogOnListView.Items[i].SubItems.Add(result.Domain);
LogOnListView.Items[i].SubItems.Add(result.StartTime.ToLocalTime().ToString());
LogOnListView.Items[i].SubItems.Add(cAD.LastLastLogon(getDN(result.SamAccountName)).ToLocalTime().ToString());
LogOnListView.Items[i].SubItems.Add(cAD.LastLastLogoff(getDN(result.SamAccountName)).ToLocalTime().ToString());
i++;
}
}
private string getDN(string samAccountName)
{
DirectoryEntry myADEntry = new DirectoryEntry();
DirectorySearcher myADSearcher = new DirectorySearcher(myADEntry);
myADSearcher.Filter = "(&(objectClass=user)(sAMAccountName=" + samAccountName + ")(!(objectClass=computer)))";
return myADSearcher.FindOne().GetDirectoryEntry().Properties["distinguishedName"].Value.ToString();
}
private List<LogonUser> getInfo()
{
List<LogonUser> LogonUserList = new List<LogonUser>();
ConnectionOptions options;
options = new ConnectionOptions();
//options.Username = "administrator";
//options.Password = "Parol-Admin";
options.EnablePrivileges = true;
options.Impersonation = ImpersonationLevel.Impersonate;
if (CBComputers.SelectedIndex == 0)
{
foreach (PC pc in CompList)
{
if (pc.Name == Environment.MachineName)
{
LogonUserList.AddRange(getLogonUserList(pc, options, LocalWMIRequestLogOnSession(), LocalWMIRequestLoggedOnUser()));
}
else
{
LogonUserList.AddRange(getLogonUserList(pc, options, WMIRequestLogOnSession(pc.IP, options), WMIRequestLoggedOnUser(pc.IP, options)));
}
}
}
else
{
PC pc = CompList[CBComputers.SelectedIndex - 1];
if (pc.Name == Environment.MachineName)
{
LogonUserList.AddRange(getLogonUserList(pc, options, LocalWMIRequestLogOnSession(), LocalWMIRequestLoggedOnUser()));
}
else
{
LogonUserList.AddRange(getLogonUserList(pc, options, WMIRequestLogOnSession(pc.IP, options), WMIRequestLoggedOnUser(pc.IP, options)));
}
}
return LogonUserList;
}
private LogonUser FindInDomainController(PC pc, ConnectionOptions options, LoggedOnUser lou, LogOnSessionR losr)
{
try
{
List<PC> Controllers = new List<PC>();
if (CBDomain.SelectedIndex == 0)
{
foreach (GlobalCatalog contr in Form1.DsDomain.Forest.GlobalCatalogs)
{
PC Con = new PC();
Con.Name = contr.Name;
Con.IP = contr.IPAddress;
Controllers.Add(Con);
}
}
else
{
foreach (DomainController contr in Form1.ListDomain[CBDomain.SelectedIndex - 1].DomainControllers)
{
PC Con = new PC();
Con.Name = contr.Name;
Con.IP = contr.IPAddress;
Controllers.Add(Con);
}
}
LogonUser getlg;
foreach (PC Controller in Controllers)
{
ManagementScope scope;
if (Controller.Name == Environment.MachineName)
{
scope = new ManagementScope("\\root\\cimv2");
}
else
{
scope = new ManagementScope("\\\\" + Controller.IP + "\\root\\cimv2", options);
scope.Connect(); //
}
String queryString = "SELECT * FROM Win32_UserAccount WHERE Domain=\"" + getDomain(lou.Antecedent) + "\" AND Name=\"" + getsAmAccountName(lou.Antecedent) + "\"";
ObjectQuery query;
query = new ObjectQuery(queryString);
ManagementObjectSearcher searcher = new ManagementObjectSearcher(scope, query);
var Temp = searcher.Get()
.Cast<ManagementObject>()
.Select(mo => new
{
Name = Convert.ToString(mo["Name"]),
FullName = Convert.ToString(mo["FullName"]),
AccountType = Convert.ToUInt32(mo["AccountType"]),
Caption = Convert.ToString(mo["Caption"]),
Description = Convert.ToString(mo["Description"]),
Disabled = Convert.ToBoolean(mo["Disabled"]),
Domain = Convert.ToString(mo["Domain"]),
InstallDate = Convert.ToDateTime(mo["InstallDate"]),
LocalAccount = Convert.ToBoolean(mo["LocalAccount"]),
Lockout = Convert.ToBoolean(mo["Lockout"]),
PasswordChangeable = Convert.ToBoolean(mo["PasswordChangeable"]),
PasswordExpires = Convert.ToBoolean(mo["PasswordExpires"]),
PasswordRequired = Convert.ToBoolean(mo["PasswordRequired"]),
SID = Convert.ToString(mo["SID"]),
SIDType = Convert.ToByte(mo["SIDType"]),
Status = Convert.ToString(mo["Status"])
}
).ToList();
if (Temp.Count == 0) { continue; }
// if (Temp.Count > 1) { throw new Exception("Найденно " + Temp.Count + " одинаковых пользователя"); }
getlg = new LogonUser()
{
AuthenticationPackage = losr.AuthenticationPackage,
Name = Temp[0].Name,
Domain = Temp[0].Domain,
Caption = Temp[0].Caption,
Description = Temp[0].Description,
InstallDate = Temp[0].InstallDate,
LogonId = losr.LogonId,
LogonType = losr.LogonType,
StartTime = losr.StartTime,
Status = Temp[0].Status,
Computer = pc.Name,
SamAccountName = getsAmAccountName(lou.Antecedent),
FullName = Temp[0].FullName,
AccountType = Temp[0].AccountType,
Enabled = !Temp[0].Disabled,
LocalAccount = Temp[0].LocalAccount,
Lockout = Temp[0].Lockout,
PasswordChangeable = Temp[0].PasswordChangeable,
PasswordExpires = Temp[0].PasswordExpires,
PasswordRequired = Temp[0].PasswordRequired,
SID = Temp[0].SID,
SIDType = Temp[0].SIDType
};
return getlg;
}
return null;
}
catch
{
}
return null;
}
public List<LogOnSessionR> LocalWMIRequestLogOnSession()
{
List<LogOnSessionR> ReqList = new List<LogOnSessionR>();
ManagementObjectSearcher searcher = new ManagementObjectSearcher("root\\CIMV2",
"SELECT * FROM Win32_LogonSession WHERE LogonType = '2' OR LogonType = '10' OR LogonType = '11'");
var Temp = searcher.Get()
.Cast<ManagementObject>()
.Select(mo => new
{
AuthenticationPackage = Convert.ToString(mo["AuthenticationPackage"]),
Name = Convert.ToString(mo["Name"]),
Caption = Convert.ToString(mo["Caption"]),
Description = Convert.ToString(mo["Description"]),
InstallDate = (mo["InstallDate"] == null ? Convert.ToDateTime(mo["InstallDate"]) : ManagementDateTimeConverter.ToDateTime(mo["InstallDate"].ToString())),
LogonId = Convert.ToString(mo["LogonId"]),
LogonType = Convert.ToUInt32(mo["LogonType"]),
StartTime = (mo["StartTime"] == null ? Convert.ToDateTime(mo["StartTime"]) : ManagementDateTimeConverter.ToDateTime(mo["StartTime"].ToString())),
Status = Convert.ToString(mo["Status"])
}
).ToList();
foreach (var obj in Temp)
{
LogOnSessionR req = new LogOnSessionR();
req.AuthenticationPackage = obj.AuthenticationPackage;
req.Name = obj.Name;
req.Caption = obj.Caption;
req.Description = obj.Description;
req.InstallDate = obj.InstallDate;
req.LogonId = obj.LogonId;
req.LogonType = obj.LogonType;
req.StartTime = obj.StartTime;
req.Status = obj.Status;
ReqList.Add(req);
}
return ReqList;
}
public List<LoggedOnUser> LocalWMIRequestLoggedOnUser()
{
List<LoggedOnUser> ReqList = new List<LoggedOnUser>();
ManagementObjectSearcher searcher = new ManagementObjectSearcher("root\\CIMV2",
"SELECT * FROM Win32_LoggedOnUser");
var Temp = searcher.Get()
.Cast<ManagementObject>()
.Select(mo => new
{
Antecedent = Convert.ToString(mo["Antecedent"]),
Dependent = Convert.ToString(mo["Dependent"])
}
).ToList();
foreach (var obj in Temp)
{
LoggedOnUser req = new LoggedOnUser();
req.Antecedent = obj.Antecedent;
req.Dependent = obj.Dependent;
ReqList.Add(req);
}
return ReqList;
}
private string getLogonId(string dependent)
{
var start = "LogonId=\"";
var end = "\"";
var regEx = new Regex(String.Format("{0}(.*){1}", Regex.Escape(start), Regex.Escape(end)));
var match = regEx.Match(dependent);
return match.Groups[1].Value;
// "\\\\.\\root\\cimv2:Win32_LogonSession.LogonId=\"997\""
}
private string getDomain(string antecedent)
{
var start = "Domain=\"";
var end = "\",";
var regEx = new Regex(String.Format("{0}(.*){1}", Regex.Escape(start), Regex.Escape(end)));
var match = regEx.Match(antecedent);
return match.Groups[1].Value;
// "\\\\.\\root\\cimv2:Win32_Account.Domain=\"LAB\" AND Name=\"Administrator\""
}
private string getsAmAccountName(string antecedent)
{
var start = "Name=\"";
var end = "\"";
var regEx = new Regex(String.Format("{0}(.*){1}", Regex.Escape(start), Regex.Escape(end)));
var match = regEx.Match(antecedent);
return match.Groups[1].Value;
//
}
private List<LogonUser> getLogonUserList(PC pc, ConnectionOptions options, List<LogOnSessionR> sessionsList, List<LoggedOnUser> loggedList)
{
List<LogonUser> getList = new List<LogonUser>();
foreach (LoggedOnUser lou in loggedList)
{
foreach(LogOnSessionR losr in sessionsList)
{
if (losr.LogonId == getLogonId(lou.Dependent))
{
LogonUser temp = FindInDomainController(pc, options, lou, losr);
if (temp != null)
{
getList.Add(temp);
}
}
}
}
return getList;
}
public List<LogOnSessionR> WMIRequestLogOnSession(string ip, ConnectionOptions options)
{
List<LogOnSessionR> ReqList = new List<LogOnSessionR>();
try
{
ManagementScope scope;
scope = new ManagementScope("\\\\" + ip + "\\root\\cimv2", options);
scope.Connect();
String queryString = "SELECT * FROM Win32_LogonSession WHERE LogonType = '2' OR LogonType = '10' OR LogonType = '11'";
ObjectQuery query;
query = new ObjectQuery(queryString);
ManagementObjectSearcher searcher = new ManagementObjectSearcher(scope, query);
var Temp = searcher.Get()
.Cast<ManagementObject>()
.Select(mo => new
{
AuthenticationPackage = Convert.ToString(mo["AuthenticationPackage"]),
Name = Convert.ToString(mo["Name"]),
Caption = Convert.ToString(mo["Caption"]),
Description = Convert.ToString(mo["Description"]),
InstallDate = (mo["InstallDate"] == null ? Convert.ToDateTime(mo["InstallDate"]) : ManagementDateTimeConverter.ToDateTime(mo["InstallDate"].ToString())),
LogonId = Convert.ToString(mo["LogonId"]),
LogonType = Convert.ToUInt32(mo["LogonType"]),
StartTime = (mo["StartTime"] == null ? Convert.ToDateTime(mo["StartTime"]) : ManagementDateTimeConverter.ToDateTime(mo["StartTime"].ToString())),
Status = Convert.ToString(mo["Status"])
}
).ToList();
foreach (var obj in Temp)
{
LogOnSessionR req = new LogOnSessionR();
req.AuthenticationPackage = obj.AuthenticationPackage;
req.Name = obj.Name;
req.Caption = obj.Caption;
req.Description = obj.Description;
req.InstallDate = obj.InstallDate;
req.LogonId = obj.LogonId;
req.LogonType = obj.LogonType;
req.StartTime = obj.StartTime;
req.Status = obj.Status;
ReqList.Add(req);
}
return ReqList;
}
catch
{
}
return new List<LogOnSessionR>();
}
public List<LoggedOnUser> WMIRequestLoggedOnUser(string ip, ConnectionOptions options)
{
List<LoggedOnUser> ReqList = new List<LoggedOnUser>();
try
{
ManagementScope scope;
scope = new ManagementScope("\\\\" + ip + "\\root\\cimv2", options);
scope.Connect();
String queryString = "SELECT * FROM Win32_LoggedOnUser";
ObjectQuery query;
query = new ObjectQuery(queryString);
ManagementObjectSearcher searcher = new ManagementObjectSearcher(scope, query);
var Temp = searcher.Get()
.Cast<ManagementObject>()
.Select(mo => new
{
Antecedent = Convert.ToString(mo["Antecedent"]),
Dependent = Convert.ToString(mo["Dependent"])
}
).ToList();
foreach (var obj in Temp)
{
LoggedOnUser req = new LoggedOnUser();
req.Antecedent = obj.Antecedent;
req.Dependent = obj.Dependent;
ReqList.Add(req);
}
return ReqList;
}
catch
{
}
return new List<LoggedOnUser>();
}
private void label2_Click(object sender, EventArgs e)
{
}
}
public class PC
{
public string Name { get; set; }
public string IP { get; set; }
}
public class LoggedOnUser
{
public string Antecedent { get; set; }
public string Dependent { get; set; }
}
public class LogOnSessionR
{
public string AuthenticationPackage { get; set; }
public string Name { get; set; }
public string Caption { get; set; }
public string Description { get; set; }
public System.DateTime InstallDate { get; set; }
public string LogonId { get; set; }
public UInt32 LogonType { get; set; }
public System.DateTime StartTime { get; set; }
public string Status { get; set; }
}
public class LogonUser
{
public string AuthenticationPackage { get; set; }
public string Name { get; set; }
public string Domain { get; set; }
public string Caption { get; set; }
public string Description { get; set; }
public System.DateTime InstallDate { get; set; }
public string LogonId { get; set; }
public UInt32 LogonType { get; set; }
public System.DateTime StartTime { get; set; }
public string Status { get; set; }
public string Computer { get; set; }
public string SamAccountName { get; set; }
public string FullName { get; set; }
public uint AccountType { get; set; }
public bool? Enabled { get; set; }
public bool LocalAccount { get; set; }
public bool Lockout { get; set; }
public bool PasswordChangeable { get; set; }
public bool PasswordExpires { get; set; }
public bool PasswordRequired { get; set; }
public string SID { get; set; }
public byte SIDType { get; set; }
}
}

If you can connect to any computer in your domain network and get any info from it - this is security issue, that must be solved.
First of all, you must understand a security problem of your solution. Any computer than you can connect to may be attacked by hacker. There are similar precedents in my experience.
So, if you need to get any info from computer or server in your domain network - you must install some agent on every computer and server, where you want to get internal data of it state. This agent will be monitor computer/server by your rule sets and send information to some database or collecting system.
Microsoft has System Center Operations Manager with this architecture and purpose. You can develop own agents, or use powershell scrips + windows scheduler to collect any data from computer.
Do not implement your solution in domain network - it's bad. A better solution is to check which computers are included in the domain and which have agents from the monitoring database. And install agents on those computers on which they do not yet exist.
Any user account or group, that has a lot of rignts on domain objects (for example, all computers) - is security issue, that must be solved.
If you want to get any logged on users - you can check process explorer.exe - which user starts it.
So, if you use powershell script as agent to collect data, you can run
query user
It will display, for expample:
user |seance |ID |Staus |Idle time | logon time |
-----|----------|---|-------|----------|----------------|
user1|rdp-tcp |2 |Active | . |12/07/2020 10:20|
then you can store this data in you database, or another storage.
If you want to develop some agent, you can use Windows API, System.Runtime.InteropServices, like this, because your agent will can access this API.
Also, you can get users, which logged on computer by this
foreach (System.Management.ManagementObject Process in Processes.Get())
{
if (Process["ExecutablePath"] != null &&
System.IO.Path.GetFileName(Process["ExecutablePath"].ToString()).ToLower() == "explorer.exe" )
{
string[] OwnerInfo = new string[2];
Process.InvokeMethod("GetOwner", (object[])OwnerInfo);
Console.WriteLine(string.Format("Windows Logged-in Interactive UserName={0}", OwnerInfo[0]));
break;
}
}
Every user start process explorer.exe, and own it.
Also, you not need to store any sp

Related

How to get disk index from USB drive name ? Please consider USB with Partition and Without Partition

I want to clean usb using diskpart from C#. I have written below code in C# to get all connect USB. And I iterate thru all usb and clean each usb using diskpart below cmd command.
diskpart
list disk
select disk <0/1/2>
clean
I want to get disk number <0/1/2> from drive name so that I can clean each usb one after another.
foreach (DriveInfo drive in DriveInfo.GetDrives())
{
if (drive.IsReady == true)
{
if (drive.DriveType == DriveType.Removable)
{
string usbName = drive.Name;
}
}
}
The following shows how to use ManagementObjectSearcher, ManagementObject to retrieve a list of removable USB drives
Create a Windows Forms App (.NET Framework) project
Add Reference (System.Management)
VS 2022:
Click Project
Select Add Reference...
Click Assemblies
Check System.Management
Click OK
Add using directives
using System.IO;
using System.Management;
using System.Diagnostics;
Create a class (name: LogicalDiskInfo)
public class LogicalDiskInfo : IComparable<LogicalDiskInfo>
{
public string Description { get; set; }
public string DeviceID { get; set; }
public uint DiskIndex { get; set; }
public uint DriveType { get; set; }
public string FileSystem { get; set; }
public bool IsRemovable { get; set; } = false;
public string Name { get; set; }
public uint PartitionIndex { get; set; }
public uint PartitionNumber { get; set; }
public UInt64 Size { get; set; }
public int CompareTo(LogicalDiskInfo other)
{
if (String.Compare(this.Name, other.Name) == 0)
return 0;
else if (String.Compare(this.Name, other.Name) < 0)
return -1;
else
return 1;
}
}
Create a class (name: LogicalDisk)
public class LogicalDisk
{
public List<LogicalDiskInfo> LogicalDiskInfos = new List<LogicalDiskInfo>();
}
Create a class (name: DiskDriveInfo)
public class DiskDriveInfo : IComparable<DiskDriveInfo>
{
public string Caption { get; set; } = string.Empty;
public string DeviceID { get; set; } = string.Empty;
public List<LogicalDiskInfo> LogicalDisks { get; set; } = new List<LogicalDiskInfo>();
public UInt32 DiskIndex { get; set; } = 0;
public string InterfaceType { get; set; } = string.Empty;
public bool IsRemovable { get; set; } = false;
public string MediaType { get; set; }
public string Model { get; set; } = string.Empty;
public string Name { get; set; } = string.Empty;
public UInt32 Partitions { get; set; } = 0;
public string PnpDeviceID { get; set; } = string.Empty;
public UInt64 Size { get; set; } = 0;
public string Status { get; set; } = string.Empty;
public int CompareTo(DiskDriveInfo other)
{
if (this.DiskIndex == other.DiskIndex)
return 0;
else if (this.DiskIndex < other.DiskIndex)
return -1;
else
return 1;
}
}
GetUSBRemovableDiskDriveInfo:
Note: In Windows 10, it's possible to create multiple partitions on a USB flash drive. See here for more info. Therefore, it's possible that more than one drive letter may exist on the same physical disk drive. The code below works with USB drives having either a single partition or multiple partitions.
private List<DiskDriveInfo> GetUSBRemovableDiskDriveInfo()
{
SortedDictionary<uint, DiskDriveInfo> diskDict = new SortedDictionary<uint, DiskDriveInfo>();
List<DiskDriveInfo> driveInfos = new List<DiskDriveInfo>();
//MediaType: 'Removable Media'
using (ManagementObjectSearcher searcherDiskDrive = new ManagementObjectSearcher("SELECT Caption, DeviceID, Index, InterfaceType, MediaType, Model, Name, Partitions, PNPDeviceID, Size, Status FROM Win32_DiskDrive WHERE InterfaceType='USB' and MediaType='Removable Media'"))
{
foreach (ManagementObject objDiskDrive in searcherDiskDrive.Get())
{
if (objDiskDrive == null)
continue;
//create new instance
DiskDriveInfo ddInfo = new DiskDriveInfo();
//set value
uint diskIndex = Convert.ToUInt32(objDiskDrive["Index"]);
ddInfo.Caption = objDiskDrive["Caption"]?.ToString();
ddInfo.DeviceID = objDiskDrive["DeviceID"]?.ToString();
ddInfo.DiskIndex = diskIndex;
ddInfo.InterfaceType = objDiskDrive["InterfaceType"]?.ToString();
ddInfo.MediaType = objDiskDrive["MediaType"]?.ToString();
ddInfo.Model = objDiskDrive["Model"]?.ToString();
ddInfo.Name = objDiskDrive["Name"]?.ToString();
ddInfo.Partitions = Convert.ToUInt32(objDiskDrive["Partitions"]);
ddInfo.PnpDeviceID = objDiskDrive["PnpDeviceID"]?.ToString();
ddInfo.Size = Convert.ToUInt64(objDiskDrive["Size"]);
ddInfo.Status = objDiskDrive["Status"]?.ToString();
if (ddInfo.MediaType == "Removable Media")
ddInfo.IsRemovable = true;
else
ddInfo.IsRemovable = false;
if (!diskDict.ContainsKey(diskIndex))
{
//Debug.WriteLine($"Adding DiskIndex {ddInfo.DiskIndex} Partitions: {ddInfo.Partitions}");
//add
diskDict.Add(diskIndex, ddInfo);
}
}
}
//create new instance
SortedDictionary<string, LogicalDisk> logicalDiskToPartitionDict = new SortedDictionary<string, LogicalDisk>();
//get info from Win32_LogicalDiskToPartition
//this is used to associate a DiskIndex and PartitionIndex with a drive letter
using (ManagementObjectSearcher searcherLogicalDiskToPartition = new ManagementObjectSearcher($#"SELECT * FROM Win32_LogicalDiskToPartition"))
{
foreach (ManagementObject objLogicalDiskToPartition in searcherLogicalDiskToPartition.Get())
{
if (objLogicalDiskToPartition == null)
continue;
string antecedent = objLogicalDiskToPartition["Antecedent"]?.ToString();
string dependent = objLogicalDiskToPartition["Dependent"]?.ToString();
string antecedentValue = antecedent.Substring(antecedent.IndexOf('=') + 1).Replace("\"", "");
uint diskIndex = 0;
uint partitionIndex = 0;
//get disk index and convert to uint
UInt32.TryParse(antecedentValue.Substring(antecedentValue.IndexOf("#") + 1, antecedentValue.IndexOf(",") - (antecedentValue.IndexOf("#") + 1)), out diskIndex);
//get partition index and convert to uint
UInt32.TryParse(antecedentValue.Substring(antecedentValue.LastIndexOf("#") + 1), out partitionIndex);
string driveLetter = dependent.Substring(dependent.IndexOf("=") + 1).Replace("\"", "");
if (diskDict.ContainsKey(diskIndex))
{
if (!logicalDiskToPartitionDict.ContainsKey(driveLetter))
{
//add
logicalDiskToPartitionDict.Add(driveLetter, new LogicalDisk());
}
//get info from Win32_LogicalDisk
using (ManagementObjectSearcher searcherLogicalDisk = new ManagementObjectSearcher($"SELECT Description, DeviceID, DriveType, FileSystem, Name, Size FROM Win32_LogicalDisk WHERE Name = '{driveLetter}'"))
{
foreach (ManagementObject objLogicalDisk in searcherLogicalDisk.Get())
{
if (objLogicalDisk == null)
continue;
//create new instance
LogicalDiskInfo logicalDiskInfo = new LogicalDiskInfo();
//set value
logicalDiskInfo.Description = objLogicalDisk["Description"]?.ToString();
logicalDiskInfo.DeviceID = objLogicalDisk["DeviceID"]?.ToString();
logicalDiskInfo.DriveType = Convert.ToUInt32(objLogicalDisk["DriveType"]);
logicalDiskInfo.DiskIndex = diskIndex;
logicalDiskInfo.FileSystem = objLogicalDisk["FileSystem"]?.ToString();
logicalDiskInfo.Name = objLogicalDisk["Name"]?.ToString();
logicalDiskInfo.PartitionIndex = partitionIndex;
logicalDiskInfo.PartitionNumber = partitionIndex + 1; //diskpart partitions start at 1
logicalDiskInfo.Size = Convert.ToUInt64(objLogicalDisk["Size"]);
//DriveType: 2=Removable; 3=Local Disk; 4=Network Drive; 5=CD
if (logicalDiskInfo.DriveType == 2)
logicalDiskInfo.IsRemovable = true;
else
logicalDiskInfo.IsRemovable = false;
Debug.WriteLine($"adding logicalDiskInfo for DiskIndex: '{diskIndex}' PartitionIndex: '{partitionIndex}' PartitionNumber: '{logicalDiskInfo.PartitionNumber}'");
//add
logicalDiskToPartitionDict[driveLetter].LogicalDiskInfos.Add(logicalDiskInfo);
}
}
}
}
}
//add logical disk info to disk dictionary
foreach(KeyValuePair<string, LogicalDisk> kvp in logicalDiskToPartitionDict)
{
List<LogicalDiskInfo> logicalDiskInfoList = kvp.Value.LogicalDiskInfos;
//sort
logicalDiskInfoList.Sort();
foreach (LogicalDiskInfo ldInfo in logicalDiskInfoList)
{
//add
diskDict[ldInfo.DiskIndex].LogicalDisks.Add(ldInfo);
}
}
//only add disks that are listed as 'Removable'
foreach(KeyValuePair<uint, DiskDriveInfo> kvp in diskDict)
{
if (kvp.Value.IsRemovable)
{
//add
driveInfos.Add(kvp.Value);
}
}
return driveInfos;
}
Usage:
System.Diagnostics.Debug.WriteLine("--------GetUSBRemovableDiskDriveInfo----------");
foreach (DiskDriveInfo ddInfo in GetUSBRemovableDiskDriveInfo())
{
string driveLetters = string.Empty;
for (int i = 0; i < ddInfo.LogicalDisks.Count; i++)
{
if (i > 0)
driveLetters += ", ";
driveLetters += ddInfo.LogicalDisks[i].Name;
}
System.Diagnostics.Debug.WriteLine($"Caption: '{ddInfo.Caption}' Name: '{ddInfo.Name}' DiskIndex: '{ddInfo.DiskIndex}' DriveLetters: [{driveLetters}] Partitions: '{ddInfo.Partitions}' Size: '{ddInfo.Size}'");
}
One can use System.Diagnostics.Process to execute a diskpart script to clean one or more disks. See this post for more info.
Resources:
System.Management.ManagementObjectSearcher
System.Management.ManagementObject
System.Diagnostics.Process
diskpart
diskpart scripts and examples
Win32_DiskDrive class
Win32_LogicalDisk class
Win32_LogicalDiskToPartition class
It may be the dirty way, but you could just use diskpart interactively and send commands / parse output. Since we are invoking diskpart anyway, a 100% managed codebase seems to not be the target as much as a way of getting it done.
So emphasis on "Dirty" way, but very simple functional way as well :-)
Sometimes when there is a perfectly good tool for the job such as ghostscript or ffmpeg, I automate them this way. Just as if I were typing at the CMD prompt.
Wrap a CMD instance in a wrapper to read/write to, and power it in event driven logic from there.
using System;
using System.Diagnostics;
namespace yourapp
{
public class cmdShell
{
private Process shellProcess;
public delegate void onDataHandler(cmdShell sender, string e);
public event onDataHandler onData;
public cmdShell()
{
try
{
shellProcess = new Process();
ProcessStartInfo si = new ProcessStartInfo("cmd.exe");
si.Arguments = "/k";
si.RedirectStandardInput = true;
si.RedirectStandardOutput = true;
si.RedirectStandardError = true;
si.UseShellExecute = false;
si.CreateNoWindow = true;
si.WorkingDirectory = Environment.GetEnvironmentVariable("windir");
shellProcess.StartInfo = si;
shellProcess.OutputDataReceived += shellProcess_OutputDataReceived;
shellProcess.ErrorDataReceived += shellProcess_ErrorDataReceived;
shellProcess.Start();
shellProcess.BeginErrorReadLine();
shellProcess.BeginOutputReadLine();
}
catch (Exception ex)
{
Trace.WriteLine(ex.Message);
}
}
void shellProcess_ErrorDataReceived(object sender, DataReceivedEventArgs e)
{
doOnData(e.Data);
}
void shellProcess_OutputDataReceived(object sender, DataReceivedEventArgs e)
{
doOnData(e.Data);
}
private void doOnData(string data)
{
if (onData != null) onData(this, data);
}
public void write(string data)
{
try
{
shellProcess.StandardInput.WriteLine(data);
}
catch (Exception ex)
{
Trace.WriteLine(ex.Message);
}
}
}
}

Call db connectivity values from a file

I am fairly new to c# and would like to know how values can be called from a file instead of statically hard coding it in the class. I know in java spring boot applications we can have it in application.properties files. In my case I have the db hostname, username and pwd stored in a file
namespace NunitTestCase
{
[TestFixture]
public class Test
{
string query = "SELECT * FROM SYSTEM.ADMIN.EMPLOYEE";
string host = "vm1.test.app.com"; //want these data in a file
int port = 5480;
string dbName = "SYSTEM";
string userName = "admin";
string password = "password";
[Test]
public void TestCase()
{
var builder = new ConnectionStringBuilder();
builder.UserName = userName;
builder.Password = password;
builder.Port = port;
builder.Host = host;
builder.Database = dbName;
using (var con = new Connection(builder.ConnectionString))
{
con.Open();
NUnit.Framework.Assert.That(con.State == ConnectionState.Open);
using (var cmd = new Command(query, con))
{
var rdr = cmd.ExecuteReader();
while (rdr.Read())
{
for (int i = 0; i < rdr.FieldCount; i++)
{
object o = null;
try
{
o = rdr.GetValue(i);
}
catch (Exception ex)
{
o = ex.Message;
}
Console.WriteLine(o);
}
}
}
con.Close();
NUnit.Framework.Assert.That(con.State == ConnectionState.Closed);
}
}
}
}
file.yaml
database:
host: "vm1.test.app.com"
port: 5480
dbName: "SYSTEM"
userName: "admin"
password: "password"
How do I make changes in my code so that instead of hardcoding, these values can be picked up from the file
Traditionally, in .net we store configuration in .json/.xml files and C# supports built-in functionality to parse it, but as far as you are using .YAML file you can install the library to parse this file:
YAMLDotNet
and use this to parse.
public class Database {
public string Host { get; set; }
public string Port { get; set; }
public string DbName { get; set; }
public string UserName { get; set; }
public string Password { get; set; }
}
public class Configuration
{
public Database Database { get; set; }
}
var yamlString = File.ReadAllText(#"...\file.yaml");
var deserializer = new DeserializerBuilder().WithNamingConvention(new CamelCaseNamingConvention()).Build();
var config = deserializer.Deserialize<Configuration>(yamlString);
If you don't want to use any libraries you can parse it manually, so create a class which reflects your model in YAML, something like:
Function to get the value of a property:
public string GetValueOfPropertyYaml(string yamlStr) {
return yamlStr?.Split(":")?.Skip(1)?.FirstOrDefault()?.Trim() ?? string.Empty;
}
Main code:
string[] yamlStringArray = File.ReadAllLines(#"..\file.yaml");
var config = new Database();
foreach (var yamlStr in yamlStringArray) {
if (yamlStr.Contains("host:")) {
config.Host = GetValueOfPropertyYaml(yamlStr);
}
if (yamlStr.Contains("port:"))
{
config.Port = GetValueOfPropertyYaml(yamlStr);
}
if (yamlStr.Contains("dbName:"))
{
config.DbName = GetValueOfPropertyYaml(yamlStr);
}
if (yamlStr.Contains("userName:"))
{
config.UserName = GetValueOfPropertyYaml(yamlStr);
}
if (yamlStr.Contains("password:"))
{
config.Password = GetValueOfPropertyYaml(yamlStr);
}
}
;
// use filled `config` variable below.
your model:
public class Database
{
public string Host { get; set; }
public string Port { get; set; }
public string DbName { get; set; }
public string UserName { get; set; }
public string Password { get; set; }
}
NOTE: I highly recommend you to use library, because it was already tested and worked perfectly(my method should be tested properly)

Return Active Directory for group members with attributes with C# .NET Core 3.1

I need to retrieve certain attributes from members of a given group in Active Directory and I think I'm on the wrong track.
My current LDAP query looks like this:
(&(memberOf:1.2.840.113556.1.4.1941:={DN for group})(objectClass=user)(objectCategory={objectCategory}))
This is a rather heavy query, which in average takes 10 - 20 seconds regardless of the result contains 0, 1 or a 1000 users. The result can be replicated in C# and powershell (Get-ADGroup -LDAPFilter {Your filter})
A colleague of mine proposed implementing something similar to this powershell query
$group = "{samAccountName}"
$attributes = "employeeId","sn","givenName","telephoneNumber","mobile","hPRnr","cn","samAccountName","gender","company","reshId"
Get-ADGroupMember -Identity $group | Get-ADUser -Properties $attributes | select $attributes
Is it possible to use the api available in C# to implement the powershell query somehow or is there a better solution?
To clearify.
I have a C# approach today which has an emphasis on LDAP. The average perfomance is between 10 - 15 seconds whether there are 0 or 1000 members in the AD group.
A complete example of how the code works with the following libraries added to the project:
Microsoft.AspNet.WebApi.Client
Microsoft.Extensions.Logging.Log4Net.AspNetCore
Newtonsoft.Json
System.DirectoryServices
System.DirectoryServices.AccountManagement
System.Runtime.Serialization.Json
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.DirectoryServices;
using Microsoft.Extensions.Logging;
namespace ActiveDirectoryLibrary.Standard.Services
{
public class LdapService
{
private ILogger _logger;
private string PersonCategory = "ObjectCategoryForUser";
public LdapService(ILogger logger)
{
_logger = logger;
}
public List<User> GetUserRecordsInNestedGroupDetailed(string nestedGroup, string ou)
{
var groupStopwatch = new Stopwatch();
groupStopwatch.Start();
var group = GetGroup(nestedGroup, ou);
groupStopwatch.Stop();
_logger.LogDebug(
$"Method {nameof(GetUserRecordsInNestedGroupDetailed)}: Getting the group {nestedGroup} took {groupStopwatch.ElapsedMilliseconds} ms");
if (group == null || !string.IsNullOrEmpty(group.DistinguishedName)) return new List<User>();
//PersonCategory is the object category for a user object in Active Directory
var ldapFilter =
$"(&(memberOf:1.2.840.113556.1.4.1941:={group.DistinguishedName})(objectClass=user)(objectCategory={PersonCategory}))";
var groupMembers = new List<User>();
using (var adsEntry = new DirectoryEntry())
{
using (var ds = new DirectorySearcher(adsEntry))
{
var stopwatch = new Stopwatch();
stopwatch.Start();
ds.Filter = ldapFilter;
ds.SearchScope = SearchScope.Subtree;
LoadAdUserProperties(ds);
var members = ds.FindAll();
stopwatch.Stop();
_logger.LogDebug(
$"Method {nameof(GetUserRecordsInNestedGroupDetailed)}: Time consumed {stopwatch.ElapsedMilliseconds} ms for {group.DistinguishedName}");
foreach (SearchResult sr in members)
{
groupMembers.Add(MapSearchResultToUser(sr));
}
}
}
return groupMembers;
}
public Group GetGroup(string samAccountName, string ou)
{
using (var entry = new DirectoryEntry($"LDAP://{ou}"))
{
var ds = new DirectorySearcher(entry)
{
Filter = "(&(objectcategory=group)(SamAccountName=" + samAccountName + "))"
};
var group = ds.FindOne();
return group == null ? null : MapSearchResultToGroup(group);
}
}
public static Group MapSearchResultToGroup(SearchResult #group)
{
var returnGroup = new Group
{
Changed = GetProperty<DateTime>(#group, "whenchanged"),
SamAccountName = GetProperty<string>(group, "SamAccountName"),
Description = GetProperty<string>(group, "Description"),
Created = GetProperty<DateTime>(group, "whencreated"),
DistinguishedName = GetProperty<string>(group, "distinguishedname"),
Name = GetProperty<string>(group, "name")
};
return returnGroup;
}
private static void LoadAdUserProperties(DirectorySearcher ds)
{
ds.PropertiesToLoad.Add("reshid");
ds.PropertiesToLoad.Add("employeeid");
ds.PropertiesToLoad.Add("sn");
ds.PropertiesToLoad.Add("givenname");
ds.PropertiesToLoad.Add("gender");
ds.PropertiesToLoad.Add("telephonenumber");
ds.PropertiesToLoad.Add("mobile");
ds.PropertiesToLoad.Add("cn");
ds.PropertiesToLoad.Add("distinguishedName");
ds.PropertiesToLoad.Add("samaccountname");
ds.PropertiesToLoad.Add("companyname");
}
public static User MapSearchResultToUser(SearchResult userProperty)
{
var reshId = GetProperty<string>(userProperty, "reshid");
var employeeElement = GetProperty<string>(userProperty, "employeeid");
var surname = GetProperty<string>(userProperty, "sn");
var givenname = GetProperty<string>(userProperty, "givenname");
var gender = GetProperty<string>(userProperty, "gender");
var phone = GetProperty<string>(userProperty, "telephonenumber");
var mobile = GetProperty<string>(userProperty, "mobile");
var hpr = GetProperty<string>(userProperty, "hprnr");
var cn = GetProperty<string>(userProperty, "cn");
var samAccountName = GetProperty<string>(userProperty, "samaccountname");
var company = GetProperty<string>(userProperty, "company");
var account = new User
{
EmployeeId = employeeElement,
Sn = surname,
GivenName = givenname,
Gender = gender,
Telephone = phone,
Mobile = mobile,
Cn = cn,
SamAccountName = samAccountName,
Company = company,
ReshId = reshId
};
return account;
}
private static T GetProperty<T>(SearchResult userProperty, string key)
{
if (userProperty.Properties[key].Count == 1)
{
return (T) userProperty.Properties[key][0];
}
return default(T);
}
public class Group
{
public DateTime Changed { get; set; }
public string SamAccountName { get; set; }
public string Description { get; set; }
public DateTime Created { get; set; }
public string DistinguishedName { get; set; }
public string Name { get; set; }
}
public class User
{
public string EmployeeId { get; set; }
public string Sn { get; set; }
public string GivenName { get; set; }
public string Telephone { get; set; }
public string OfficePhone { get; set; }
public string Mobile { get; set; }
public string Mail { get; set; }
public string Cn { get; set; }
public string SamAccountName { get; set; }
public string Gender { get; set; }
public string Company { get; set; }
public string ReshId { get; set; }
}
}
}
I've written about this in an article I wrote about finding members of a group, since group membership can be an oddly complicated thing sometimes. But here is a method that I put there that will likely be good enough for your case.
I've modified it to return a User object like you are in your code. If you pass true for the recursive parameter, it will traverse nested groups. You should be able to modify it to suit your needs.
public static IEnumerable<User> GetGroupMemberList(DirectoryEntry group, bool recursive = false) {
var members = new List<User>();
group.RefreshCache(new[] { "member" });
while (true) {
var memberDns = group.Properties["member"];
foreach (string member in memberDns) {
using (var memberDe = new DirectoryEntry($"LDAP://{member.Replace("/", "\\/")}")) {
memberDe.RefreshCache(new[] { "objectClass", "samAccountName", "mail", "mobile" });
if (recursive && memberDe.Properties["objectClass"].Contains("group")) {
members.AddRange(GetGroupMemberList(memberDe, true));
} else {
members.Add(new User {
SamAccountName = (string) memberDe.Properties["samAccountName"].Value,
Mail = (string) memberDe.Properties["mail"].Value,
Mobile = (string) memberDe.Properties["mobile"].Value,
});
}
}
}
if (memberDns.Count == 0) break;
try {
group.RefreshCache(new[] {$"member;range={members.Count}-*"});
} catch (COMException e) {
if (e.ErrorCode == unchecked((int) 0x80072020)) { //no more results
break;
}
throw;
}
}
return members;
}
You do have to pass it a DirectoryEntry object for the group. If you already have the DN of the group, you can create it like this:
new DirectoryEntry($"LDAP://{dn.Replace("/", "\\/")}")
If you don't, you can find the group by its sAMAccountName like this:
var groupSamAccountName = "MyGroup";
var ds = new DirectorySearcher($"(sAMAccountName={groupSamAccountName})") {
PropertiesToLoad = { "cn" } //just to stop it from returning every attribute
};
var groupDirectoryEntry = ds.FindOne()?.GetDirectoryEntry();
var members = GetGroupMemberList(groupDirectoryEntry, false); //pass true if you want recursive members
There is other code in my article for finding members from external trusted domains (if you have any trusted domains) and for finding users who have the group as their primary group, since the primary group relationship isn't visible in the group's member attribute.
To use this code in .NET Core, you need to install the Microsoft.Windows.Compatibility NuGet package to be able to use the System.DirectoryServices namespace. This will limit you to only being able to run your application on Windows. If you need to run your app on non-Windows operating systems, you can look into the Novell.Directory.Ldap.NETStandard, but I can't help there.
Since my current answer diverges heavily from Gabriel Lucis, I figured it be better to propose what I came up with:
public IEnumerable<User> GetContactDetailsForGroupMembersWithPrincipalContext(string samAccountName, string ou, bool recursive)
{
var ctx = new PrincipalContext(ContextType.Domain);
var grp = GroupPrincipal.FindByIdentity(ctx, IdentityType.SamAccountName, samAccountName);
var users = new List<User>();
if (grp != null)
{
foreach (var principal in grp.GetMembers(true))
{
var member = (UserPrincipal) principal;
var user = GetUser(member);
if (user != null)
{
users.Add(user);
}
}
}
return users;
}
private User GetUser(UserPrincipal member)
{
var entry = (DirectoryEntry) member.GetUnderlyingObject();
var search = new DirectorySearcher(entry);
search.PropertiesToLoad.Add("samAccountName");
search.PropertiesToLoad.Add("mail");
search.PropertiesToLoad.Add("mobile");
var result = search.FindOne();
var user = MapSearchResultToUser(result);
return user;
}
public static User MapSearchResultToUser(SearchResult userProperty)
{
var mobile = GetProperty<string>(userProperty, "mobile");
var mail = GetProperty<string>(userProperty, "mail");
var samAccountName = GetProperty<string>(userProperty, "samaccountname");
var account = new User
{
Mobile = mobile,
Mail = mail,
SamAccountName = samAccountName,
};
return account;
}
private static T GetProperty<T>(SearchResult userProperty, string key)
{
if (userProperty.Properties[key].Count == 1)
{
return (T)userProperty.Properties[key][0];
}
return default(T);
}
The average performance up to 50 members is about 500 to 1000 ms. The Code does not scale very well, a nested group with 1079 members had in average 13 000 ms.
The reason why I have to query AD two times:
Find the AD Group to get the group members
Get custom properties from the User which are not available in the UserPrincipal object.

Compare data from ActiveDirectory and store to db

Currently I create that ActiveDirectory user is store to file and it's working perfect. What I want for now is that I need to comapre data from ActiveDirectory and data from database and if status is 0 change it and store to database result. It means if account is active = 1 is not active = 0.
So far I create this
using Newtonsoft.Json;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Data;
using System.Data.SqlClient;
using System.DirectoryServices;
using System.IO;
namespace ActiverDirectory
{
public class Korisnik
{
public int Id { get; set; }
public string Username { get; set; } //samaccountname
public string DisplayName { get; set; } //displayname
public bool isEnabled { get; set; } //AccountExpires
public bool PassNevExp { get; set; } //PassExpires
}
class Program
{
static void Main(string[] args)
{
foreach (Korisnik korisnik in VratiKorisnike())
{
Console.WriteLine(korisnik);
}
}
public static List<Korisnik> VratiKorisnike()
{
List<Korisnik> lstADUsers = new List<Korisnik>();
string sDomainName = "saoos";
string DomainPath = "LDAP://" + sDomainName;
string constring = #"Data Source=(LocalDb)\MSSQLLocalDB;Initial Catalog=DesignSaoOsig1;Integrated Security=True";
string Query = "SELECT * FROM tblZaposleni_AD";
DataTable table = new DataTable();
SqlDataAdapter adapter = new SqlDataAdapter(Query, constring);
adapter.Fill(table);
string txt = "";
string fileLoc = #"C:\output.txt";
foreach (DataRow row in table.Rows)
{
string line = "";
foreach (DataColumn column in table.Columns)
{
line += "," + row[column.ColumnName].ToString();
}
txt += line.Substring(1);
}
using (var sw = new StreamWriter(fileLoc))
{
sw.WriteLine(txt);
}
Console.WriteLine("Ok");
DirectoryEntry searchRoot = new DirectoryEntry(DomainPath);
DirectorySearcher search = new DirectorySearcher(searchRoot);
search.Filter = "(&(objectClass=user)(objectCategory=person))";
search.PropertiesToLoad.Add("samaccountname"); // Username
search.PropertiesToLoad.Add("displayname"); // display name
search.PropertiesToLoad.Add("userAccountControl"); // isEnabled
search.PropertiesToLoad.Add("pwdLastSet"); //passwordExpires
DataTable resultsTable = new DataTable();
resultsTable.Columns.Add("samaccountname");
resultsTable.Columns.Add("displayname");
resultsTable.Columns.Add("Neaktivan");
resultsTable.Columns.Add("dontexpirepassword");
SearchResult result;
SearchResultCollection resultCol = search.FindAll();
if (resultCol != null)
{
for (int counter = 0; counter < resultCol.Count; counter++)
{
string UserNameEmailString = string.Empty;
result = resultCol[counter];
if (result.Properties.Contains("samaccountname")
&& result.Properties.Contains("displayname"))
{
int userAccountControl = Convert.ToInt32(result.Properties["userAccountControl"][0]);
string samAccountName = Convert.ToString(result.Properties["samAccountName"][0]);
int isEnable;
int Dont_Expire_Password;
if (userAccountControl > 0)
{
isEnable = 0;
}
else
{
isEnable = 1;
}
if ((userAccountControl & 65536) != 0)
{
Dont_Expire_Password = 1;
}
else
{
Dont_Expire_Password = 0;
}
Korisnik korisnik = new Korisnik();
korisnik.Username = (result.Properties["samaccountname"][0]).ToString();
korisnik.DisplayName = result.Properties["displayname"][0].ToString();
korisnik.isEnabled = Convert.ToBoolean(result.Properties["userAccountControl"][0]);
//long value = (long)result.Properties["pwdLastSet"][0];
//DateTime pwdLastSet = DateTime.FromFileTimeUtc(value);
DataRow dr = resultsTable.NewRow();
dr["samaccountname"] = korisnik.Username.ToString();
dr["displayname"] = korisnik.DisplayName.ToString();
dr["neaktivan"] = Math.Abs(isEnable);
dr["dontexpirepassword"] = Dont_Expire_Password;
resultsTable.Rows.Add(dr);
lstADUsers.Add(korisnik);
}
}
var json = JsonConvert.SerializeObject(resultCol, Formatting.Indented);
var res = json;
Console.WriteLine("Ispis uspjesno obavljen");
Console.ReadLine();
File.WriteAllText(fileLoc, json);
}
return lstADUsers;
}
}
}
So far everything is here, I just need to store this to [db].[table]
This is some peace of code which check from ActiveDirectory if UserAccount is active or disable
If account is active in ActiveDirectory and in [db].[table] is inactive change status to 1 and store data. When I run this I get properly output, but status is not changed.
Any ideas
Try something like this :
public class Korisnik
{
public int Id { get; set; }
public string Username { get; set; } //samaccountname
public string DisplayName { get; set; } //displayname
private bool _isEnabled { get; set; } //AccountExpires
public int isEnabled {
get { return (_isEnabled == true)? 1 : 0;}
set { _isEnabled = (value == 0) ? false : true; }
} //AccountExpires
public bool PassNevExp { get; set; } //PassExpires
}

How to export Windows Event log for a specific source with WMI?

I'm trying to use Win32_NTEventLogFile WMI class to export the Applications branch of the Windows Event log, but filter it only for a specific source. (Otherwise the log contains too much unnecessary information.)
So, just as an example, say, I need to export all records that contain MSSQL$SQLSRVR2012:
I do this:
using System.Management;
static void Main(string[] args)
{
BackupEventLogFilterBySource("Application", "MSSQL$SQLSRVR2012", #"C:\Users\User\Desktop\exp.evtx");
}
public static void BackupEventLogFilterBySource(String logName, String applicationName, String targetFile)
{
ManagementScope scope = new ManagementScope(#"\\.\root\cimv2");
scope.Options.EnablePrivileges = true;
scope.Options.Impersonation = ImpersonationLevel.Impersonate;
ObjectQuery query = new ObjectQuery(
String.Format("Select * from Win32_NTEventLogFile Where LogFileName='{0}' And Sources='{1}'",
logName, applicationName)
);
using (ManagementObjectSearcher search =
new ManagementObjectSearcher(scope, query))
{
foreach (ManagementObject o in search.Get())
{
ManagementBaseObject inParams = o.GetMethodParameters("BackupEventlog");
inParams["ArchiveFileName"] = targetFile;
ManagementBaseObject outParams = o.InvokeMethod("BackupEventLog", inParams, null);
var res = outParams.Properties["ReturnValue"].Value;
Console.Write("result=" + res + "\n");
}
}
}
But that query fails with the following exception:
An unhandled exception of type 'System.Management.ManagementException'
occurred in System.Management.dll
Additional information: Invalid query
So what am I doing wrong here?
The internal name used to identify the Source may be different from what is presented in the Computer Management UI.
For example, the Source Winlogon, internally is referenced as Microsoft-Windows-Winlogon.
Also, there's a problem with the Sources parameter, since it's an array.
This modified method uses Win32_NTLogEvent instead of Win32_NTEventLogFile.
I think it goes more directly to the target.
The query uses LIKE '%parameter%' to filter the Source, for the reason I mentioned. It's however possible to extract all the sources names using your original method with the LogFileName filter and analyse the content of the Sources { } array.
The values extracted from the Log Source File are store in a List.
You can use it's properties to create a report that looks like the one you see in Event Viewer.
Note: The TimeGenerated and TimeLogged properties can be converted to DateTime using the ManagementDateTimeConverter
.ToDateTime Method
public class WinLogEvent
{
public string ComputerName { get; set; }
public string LogName { get; set; }
public string Message { get; set; }
public string Source { get; set; }
public UInt16 EventCode { get; set; }
public uint EventIdentifier { get; set; }
public string EventType { get; set; }
public uint RecordNumber { get; set; }
public DateTime? TimeGenerated { get; set; }
public DateTime? TimeLogged { get; set; }
public byte[] Data { get; set; }
public string[] InsertionStrings { get; set; }
}
private static EnumerationOptions GetEnumerationOptions(bool deepScan)
{
var mOptions = new EnumerationOptions()
{
Rewindable = false, //Forward only query => no caching
ReturnImmediately = true, //Pseudo-async result
DirectRead = true,
EnumerateDeep = deepScan
};
return mOptions;
}
private static ConnectionOptions GetConnectionOptions(string UserName, string Password, string Domain)
{
var connOptions = new ConnectionOptions()
{
EnablePrivileges = true,
Timeout = ManagementOptions.InfiniteTimeout,
Authentication = AuthenticationLevel.PacketPrivacy,
Impersonation = ImpersonationLevel.Default,
Username = UserName,
Password = Password,
//Authority = "NTLMDOMAIN:[domain]"
Authority = !string.IsNullOrEmpty(Domain) ? $"NTLMDOMAIN:{Domain}" : string.Empty
};
return connOptions;
}
public static List<WinLogEvent> BackupEventLogFilterBySource(string logName, string sourceName)
{
List<WinLogEvent> logEvents = new List<WinLogEvent>();
var connOptions = GetConnectionOptions(null, null, null);
var options = GetEnumerationOptions(false);
var scope = new ManagementScope(#"\\" + Environment.MachineName + #"\root\CIMV2", connOptions);
scope.Connect();
var query = new SelectQuery("SELECT * FROM Win32_NTLogEvent");
query.Condition = $"Logfile='{logName}' AND SourceName LIKE '%{sourceName}%'";
using (ManagementObjectSearcher moSearch = new ManagementObjectSearcher(scope, query, options))
{
foreach (ManagementObject eventLog in moSearch.Get())
{
ManagementBaseObject inParams = eventLog.GetMethodParameters("BackupEventlog");
inParams["ArchiveFileName"] = #"D:\exp.evtx";
ManagementBaseObject outParams = eventLog.InvokeMethod("BackupEventLog", inParams, null);
var res = outParams.Properties["ReturnValue"].Value;
logEvents.Add(new WinLogEvent
{
ComputerName = eventLog.GetPropertyValue("ComputerName")?.ToString(),
LogName = eventLog.GetPropertyValue("Logfile")?.ToString(),
Source = eventLog.GetPropertyValue("SourceName")?.ToString(),
EventCode = (UInt16?)eventLog.GetPropertyValue("EventCode") ?? 0,
EventIdentifier = (uint?)eventLog.GetPropertyValue("EventIdentifier") ?? 0,
EventType = eventLog.GetPropertyValue("Type")?.ToString(),
RecordNumber = (uint?)eventLog.GetPropertyValue("RecordNumber") ?? 0,
TimeGenerated = ManagementDateTimeConverter.ToDateTime(eventLog.GetPropertyValue("TimeGenerated")?.ToString()),
TimeLogged = ManagementDateTimeConverter.ToDateTime(eventLog.GetPropertyValue("TimeWritten")?.ToString()),
Message = eventLog.GetPropertyValue("Message")?.ToString(),
InsertionStrings = (string[])eventLog.GetPropertyValue("InsertionStrings") ?? null,
Data = (byte[])eventLog.GetPropertyValue("Data") ?? null,
});
inParams.Dispose();
outParams.Dispose();
}
}
return logEvents;
} //BackupEventLogFilterBySource
In your query, you have to replace sources to SourceName. In order to get the right WMI query use wbemtest, it will exactly let you know which field to query.

Categories