using System; using System.Collections; using System.Collections.Generic; using System.ComponentModel; using System.Linq; using Vanara.Extensions; using Vanara.InteropServices; using Vanara.PInvoke; using static Vanara.PInvoke.NetApi32; namespace Vanara { /// Represents a user account on a server. [DefaultProperty(nameof(UserName))] public sealed class UserAccount : IEquatable, INamedEntity { /// The base date internal static readonly DateTime baseDate = new(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc); /// Initializes a new instance of the class. /// Name of the user. /// The target. internal UserAccount(string userName, string target) { UserName = userName; Target = target; } /// /// This member can be one or more of the following values. /// /// Note that setting user account control flags may require certain privileges and control access rights. For more information, see /// the Remarks section of the NetUserSetInfo function. /// /// /// /// Value /// Meaning /// /// /// UF_SCRIPT /// The logon script executed. This value must be set. /// /// /// UF_ACCOUNTDISABLE /// The user's account is disabled. /// /// /// UF_HOMEDIR_REQUIRED /// The home directory is required. This value is ignored. /// /// /// UF_PASSWD_NOTREQD /// No password is required. /// /// /// UF_PASSWD_CANT_CHANGE /// The user cannot change the password. /// /// /// UF_LOCKOUT /// /// The account is currently locked out. You can call the NetUserSetInfo function to clear this value and unlock a previously locked /// account. You cannot use this value to lock a previously unlocked account. /// /// /// /// UF_DONT_EXPIRE_PASSWD /// The password should never expire on the account. /// /// /// UF_ENCRYPTED_TEXT_PASSWORD_ALLOWED /// The user's password is stored under reversible encryption in the Active Directory. /// /// /// UF_NOT_DELEGATED /// Marks the account as "sensitive"; other users cannot act as delegates of this user account. /// /// /// UF_SMARTCARD_REQUIRED /// Requires the user to log on to the user account with a smart card. /// /// /// UF_USE_DES_KEY_ONLY /// Restrict this principal to use only Data Encryption Standard (DES) encryption types for keys. /// /// /// UF_DONT_REQUIRE_PREAUTH /// This account does not require Kerberos preauthentication for logon. /// /// /// UF_TRUSTED_FOR_DELEGATION /// /// The account is enabled for delegation. This is a security-sensitive setting; accounts with this option enabled should be tightly /// controlled. This setting allows a service running under the account to assume a client's identity and authenticate as that user /// to other remote servers on the network. /// /// /// /// UF_PASSWORD_EXPIRED /// The user's password has expired. Windows 2000: This value is ignored. /// /// /// UF_TRUSTED_TO_AUTHENTICATE_FOR_DELEGATION /// /// The account is trusted to authenticate a user outside of the Kerberos security package and delegate that user through /// constrained delegation. This is a security-sensitive setting; accounts with this option enabled should be tightly controlled. /// This setting allows a service running under the account to assert a client's identity and authenticate as that user to /// specifically configured services on the network. Windows XP/2000: This value is ignored. /// /// /// /// /// The following values describe the account type. Only one value can be set. You cannot change the account type using the /// NetUserSetInfo function. /// /// /// /// Value /// Meaning /// /// /// UF_NORMAL_ACCOUNT /// This is a default account type that represents a typical user. /// /// /// UF_TEMP_DUPLICATE_ACCOUNT /// /// This is an account for users whose primary account is in another domain. This account provides user access to this domain, but /// not to any domain that trusts this domain. The User Manager refers to this account type as a local user account. /// /// /// /// UF_WORKSTATION_TRUST_ACCOUNT /// This is a computer account for a computer that is a member of this domain. /// /// /// UF_SERVER_TRUST_ACCOUNT /// This is a computer account for a backup domain controller that is a member of this domain. /// /// /// UF_INTERDOMAIN_TRUST_ACCOUNT /// This is a permit to trust account for a domain that trusts other domains. /// /// /// /// The account control flags. public UserAcctCtrlFlags AccountControlFlags { get => NetUserGetInfo(Target, UserName).usri1_flags; set => NetUserSetInfo(Target, UserName, new USER_INFO_1008 { usri1008_flags = value }); } /// The date and time when the account expires. A value of indicates that the account never expires. /// The account expiration. public DateTime? AccountExpiration { get => DWORDtoDT(NetUserGetInfo(Target, UserName).usri2_acct_expires); set => NetUserSetInfo(Target, UserName, new USER_INFO_1017 { usri1017_acct_expires = DTtoDWORD(value) }); } /// /// A string that is reserved for use by applications. This string can be a string, or it can have any number /// of characters. Microsoft products use this member to store user configuration information. Do not modify this information. /// /// The parms. public string AppParams { get => NetUserGetInfo(Target, UserName).usri11_parms; set => NetUserSetInfo(Target, UserName, new USER_INFO_1013 { usri1013_parms = value }); } /// /// /// The number of times the user tried to log on to the account using an incorrect password. A value of – 1 indicates that the value /// is unknown. Calls to the NetUserAdd and NetUserSetInfo functions ignore this member. /// /// /// This member is replicated from the primary domain controller (PDC); it is also maintained on each backup domain controller (BDC) /// in the domain. To obtain an accurate value, you must query each BDC in the domain. The number of times the user tried to log on /// using an incorrect password is the largest value retrieved. /// /// /// The bad password count. public uint BadPasswordCount => NetUserGetInfo(Target, UserName).usri11_bad_pw_count; /// The code page for the user's language of choice. /// The code page. public uint CodePage { get => NetUserGetInfo(Target, UserName).usri11_code_page; set => NetUserSetInfo(Target, UserName, new USER_INFO_1025 { usri1025_code_page = value }); } /// /// A string that contains a comment to associate with the user account. The string can be a string, or it /// can have any number of characters. /// /// The comment. public string Comment { get => NetUserGetInfo(Target, UserName).usri10_comment; set => NetUserSetInfo(Target, UserName, new USER_INFO_1007 { usri1007_comment = value }); } /// The country/region code for the user's language of choice. /// The country code. public uint CountryCode { get => NetUserGetInfo(Target, UserName).usri11_country_code; set => NetUserSetInfo(Target, UserName, new USER_INFO_1024 { usri1024_country_code = value }); } /// /// A string that contains the full name of the user. This string can be a string, or it can have any number /// of characters. /// /// The full name. public string FullName { get => NetUserGetInfo(Target, UserName).usri10_full_name; set => NetUserSetInfo(Target, UserName, new USER_INFO_1011 { usri1011_full_name = value }); } /// Retrieves a list of global groups to which a specified user belongs. /// A sequence of global groups to which this user belongs. /// /// /// If you call this function on a domain controller that is running Active Directory, access is allowed or denied based on the /// access control list (ACL) for the securable object. The default ACL permits all authenticated users and members of the /// "Pre-Windows 2000 compatible access" group to view the information. If you call this function on a member server or workstation, /// all authenticated users can view the information. For information about anonymous access and restricting anonymous access on /// these platforms, see Security Requirements for the Network Management Functions. For more information on ACLs, ACEs, and access /// tokens, see Access Control Model. /// /// The security descriptor of the User object is used to perform the access check for this function. /// public IEnumerable GlobalGroups => NetUserGetGroups(Target, UserName).Select(i => i.grui0_name); /// A string that specifies the drive letter assigned to the user's home directory for logon purposes. /// The home directory drive letter. public string HomeDirectoryDriveLetter { get => NetUserGetInfo(Target, UserName).usri3_home_dir_drive; set => NetUserSetInfo(Target, UserName, new USER_INFO_1053 { usri1053_home_dir_drive = value }); } /// /// A string specifying the path of the home directory of the user specified by the UserName member. The string can be . /// /// The home folder. public string HomeFolder { get => NetUserGetInfo(Target, UserName).usri1_home_dir; set => NetUserSetInfo(Target, UserName, new USER_INFO_1006 { usri1006_home_dir = value }); } /// /// This member is currently not used. /// The date and time when the last logoff occurred. /// /// This member is maintained separately on each backup domain controller (BDC) in the domain. To obtain an accurate value, you must /// query each BDC in the domain. The last logoff occurred at the time indicated by the largest retrieved value. /// /// /// The last logoff. public DateTime? LastLogoff => DWORDtoDT(NetUserGetInfo(Target, UserName).usri11_last_logoff); /// /// The date and time when the last logon occurred. /// /// This member is maintained separately on each backup domain controller (BDC) in the domain. To obtain an accurate value, you must /// query each BDC in the domain. The last logon occurred at the time indicated by the largest retrieved value. /// /// /// The last logon. public DateTime LastLogon => DWORDtoDT(NetUserGetInfo(Target, UserName).usri11_last_logon).GetValueOrDefault(); /// /// Retrieves a list of local groups to which a specified user belongs, including local groups in which the user is indirectly a /// member (that is, the user has membership in a global group that is itself a member of one or more local groups). /// /// A sequence of local groups to which this user belongs. /// /// /// If you call this function on a domain controller that is running Active Directory, access is allowed or denied based on the /// access control list (ACL) for the securable object. The default ACL permits all authenticated users and members of the /// "Pre-Windows 2000 compatible access" group to view the information. If you call this function on a member server or workstation, /// all authenticated users can view the information. For information about anonymous access and restricting anonymous access on /// these platforms, see Security Requirements for the Network Management Functions. For more information on ACLs, ACEs, and access /// tokens, see Access Control Model. /// /// /// The security descriptor of the Domain object is used to perform the access check for this function. The caller must have Read /// Property permission on the Domain object. /// /// public IEnumerable LocalGroups => NetUserGetLocalGroups(Target, UserName, GetLocalGroupFlags.LG_INCLUDE_INDIRECT).Select(i => i.lgrui0_name); /// /// /// A pointer to a 168-bit array that specifies the times during which the user can log on. Each bit represents a unique hour in the /// week, in Greenwich Mean Time (GMT). /// /// /// The first bit (bit 0, word 0) is Sunday, 0:00 to 0:59; the second bit (bit 1, word 0) is Sunday, 1:00 to 1:59; and so on. Note /// that bit 0 in word 0 represents Sunday from 0:00 to 0:59 only if you are in the GMT time zone. In all other cases you must /// adjust the bits according to your time zone offset (for example, GMT minus 8 hours for Pacific Standard Time). /// /// /// Specify in this member when calling the NetUserAdd function to indicate no time restriction. Specify a /// pointer when calling the NetUserSetInfo function to indicate that no change is to be made to the times /// during which the user can log on. /// /// /// The logon hours. /// LogonHours public BitArray LogonHours { get { IntPtr ptr = NetUserGetInfo(Target, UserName).usri11_logon_hours; return new BitArray(ptr.ToArray(21)); } set { if (value is null) { NetUserSetInfo(Target, UserName, new USER_INFO_1020 { usri1020_units_per_week = UnitsPerWeek }); return; } if (value.Count != 168) { throw new ArgumentOutOfRangeException(nameof(LogonHours)); } byte[] bytes = new byte[21]; value.CopyTo(bytes, 0); using SafeByteArray mem = new(bytes); NetUserSetInfo(Target, UserName, new USER_INFO_1020 { usri1020_units_per_week = UnitsPerWeek, usri1020_logon_hours = mem.DangerousGetHandle() }); } } /// /// /// A string that contains the name of the server to which logon requests are sent. Server names should be preceded by two /// backslashes (\). To indicate that the logon request can be handled by any logon server, specify an asterisk (\*) for the server /// name. A string indicates that requests should be sent to the domain controller. /// /// For Windows servers, the NetUserGetInfo function returns \*. /// /// The logon server. public string LogonServer { get => NetUserGetInfo(Target, UserName).usri11_logon_server; set => NetUserSetInfo(Target, UserName, new USER_INFO_1023 { usri1023_logon_server = value }); } /// /// The maximum amount of disk space the user can use. Specify to use all available disk space. /// /// The maximum storage. public uint MaxStorage { get => NetUserGetInfo(Target, UserName).usri11_max_storage; set => NetUserSetInfo(Target, UserName, new USER_INFO_1018 { usri1018_max_storage = value }); } /// /// The user's operator privileges. /// /// For the NetUserGetInfo function, the appropriate value is returned based on the local group membership. If the user is a member /// of Print Operators, AF_OP_PRINT is set. If the user is a member of Server Operators, AF_OP_SERVER is set. If the user is a /// member of the Account Operators, AF_OP_ACCOUNTS is set. AF_OP_COMM is never set. /// /// This member can be one or more of the following values. /// /// /// Value /// Meaning /// /// /// AF_OP_PRINT /// The print operator privilege is enabled. /// /// /// AF_OP_COMM /// The communications operator privilege is enabled. /// /// /// AF_OP_SERVER /// The server operator privilege is enabled. /// /// /// AF_OP_ACCOUNTS /// The accounts operator privilege is enabled. /// /// /// /// The operator privileges. public UserOpPriv OperatorPrivileges { get => NetUserGetInfo(Target, UserName).usri11_auth_flags; set => NetUserSetInfo(Target, UserName, new USER_INFO_1010 { usri1010_auth_flags = value }); } /// /// A string that specifies the password for the user identified by the UserName member. The length cannot exceed PWLEN bytes. /// /// The password. public string Password { set => NetUserSetInfo(Target, UserName, new USER_INFO_1003 { usri1003_password = value }); } /// The time elapsed since the Password member was last changed. /// The password age. public TimeSpan PasswordAge => TimeSpan.FromSeconds(NetUserGetInfo(Target, UserName).usri1_password_age); /// /// The password expiration information. /// The NetUserGetInfo function return zero if the password has not expired (and nonzero if it has). /// /// When you call NetUserAdd or NetUserSetInfo, specify a nonzero value in this member to inform users that they must change their /// password at the next logon. To turn off this message, call NetUserSetInfo and specify zero in this member. Note that you /// cannot specify zero to negate the expiration of a password that has already expired. /// /// /// if [password expired]; otherwise, . public bool PasswordExpired => NetUserGetInfo(Target, UserName).usri3_password_expired != 0; /// /// The relative identifier (RID) of the Primary Global Group for the user. When you call the NetUserAdd function, this /// member must be DOMAIN_GROUP_RID_USERS (defined in WinNT.h). When you call NetUserSetInfo, this member must be the RID of /// a global group in which the user is enrolled. For more information, see Well-Known SIDs and SID Components. /// /// The primary group identifier. public uint PrimaryGroupId { get => NetUserGetInfo(Target, UserName).usri3_primary_group_id; set => NetUserSetInfo(Target, UserName, new USER_INFO_1051 { usri1051_primary_group_id = value }); } /// /// /// The level of privilege assigned to the UserName member. For more information about user and group account rights, see Privileges. /// /// /// /// Value /// Meaning /// /// /// USER_PRIV_GUEST /// Guest /// /// /// USER_PRIV_USER /// User /// /// /// USER_PRIV_ADMIN /// Administrator /// /// /// /// The privilege. public UserPrivilege Privilege => NetUserGetInfo(Target, UserName).usri1_priv; /// /// A string that specifies a path to the user's profile. This value can be a string, a local absolute path, /// or a UNC path. /// /// The profile path. public string ProfilePath { get => NetUserGetInfo(Target, UserName).usri3_profile; set => NetUserSetInfo(Target, UserName, new USER_INFO_1052 { usri1052_profile = value }); } /// /// A string specifying the path for the user's logon script file. The script file can be a .CMD file, an .EXE file, or a .BAT file. /// The string can also be . /// /// The script path. public string ScriptPath { get => NetUserGetInfo(Target, UserName).usri1_script_path; set => NetUserSetInfo(Target, UserName, new USER_INFO_1009 { usri1009_script_path = value }); } /// /// The number of times the user logged on successfully to this account. A value of – 1 indicates that the value is unknown. /// /// This member is maintained separately on each backup domain controller (BDC) in the domain. To obtain an accurate value, you must /// query each BDC in the domain. The number of times the user logged on successfully is the sum of the retrieved values. /// /// /// The successful logons. public uint SuccessfulLogons => NetUserGetInfo(Target, UserName).usri11_num_logons; /// /// Gets a string that specifies the DNS or NetBIOS name of the remote server on which the user account resides. If this value is /// , the local computer is assumed. /// /// The target. public string Target { get; } /// /// /// The number of equal-length time units into which the week is divided. This value is required to compute the length of the bit /// string in the logon_hours member. /// /// This value must be UNITS_PER_WEEK for LAN Manager 2.0. This element is ignored by the NetUserAdd and NetUserSetInfo functions. /// For service applications, the units must be one of the following values: SAM_DAYS_PER_WEEK, SAM_HOURS_PER_WEEK, or SAM_MINUTES_PER_WEEK. /// /// The units per week. public uint UnitsPerWeek { get => NetUserGetInfo(Target, UserName).usri11_units_per_week; set => NetUserSetInfo(Target, UserName, new USER_INFO_1020 { usri1020_units_per_week = value, usri1020_logon_hours = IntPtr.Zero }); } /// /// A string that contains a user comment. This string can be a string, or it can have any number of characters. /// /// The user comment. public string UserComment { get => NetUserGetInfo(Target, UserName).usri10_usr_comment; set => NetUserSetInfo(Target, UserName, new USER_INFO_1012 { usri1012_usr_comment = value }); } /// A string that specifies the name of the user account. /// The name of the user. public string UserName { get; } /// A pointer to a SID structure that contains the security identifier (SID) that uniquely identifies the user. /// The user sid. public PSID UserSid => NetUserGetInfo(Target, UserName).usri23_user_sid; /// /// An array of strings that contains the names of workstations from which the user can log on. As many as eight workstations can be /// specified. If you do not want to restrict the number of workstations, use a value. To disable logons from /// all workstations to this account, set the UF_ACCOUNTDISABLE value in the AccountControlFlags member. /// /// The workstations. public string[] Workstations { get => NetUserGetInfo(Target, UserName).usri11_workstations?.Split(','); set => NetUserSetInfo(Target, UserName, new USER_INFO_1014 { usri1014_workstations = value is null || value.Length == 0 ? null : string.Join(",", value) }); } /// string INamedEntity.Name => UserName; /// Changes the password for a specified network server or domain. /// A string that specifies the user's old password. /// A string that specifies the user's new password. /// /// A string that specifies the DNS or NetBIOS name of a remote server or domain on which the function is to execute. If this /// parameter is NULL, the logon domain of the caller is used. /// /// /// /// If an application calls the NetUserChangePassword function on a domain controller that is running Active Directory, /// access is allowed or denied based on the access control list (ACL) for the securable object. The default ACL permits only Domain /// Admins and Account Operators to call this function. On a member server or workstation, only Administrators and Power Users can /// call this function. A user can change his or her own password. For more information, see Security Requirements for the Network /// Management Functions. For more information on ACLs, ACEs, and access tokens, see Access Control Model. /// /// /// The security descriptor of the User object is used to perform the access check for this function. In addition, the caller must /// have the "Change password" control access right on the User object. This right is granted to Anonymous Logon and Everyone by default. /// /// Note that for the function to succeed, the oldpassword parameter must match the password as it currently exists. /// /// In some cases, the process that calls the NetUserChangePassword function must also have the SE_CHANGE_NOTIFY_NAME /// privilege enabled; otherwise, NetUserChangePassword fails and GetLastError returns ERROR_ACCESS_DENIED. This privilege is /// not required for the LocalSystem account or for accounts that are members of the administrators group. By default, /// SE_CHANGE_NOTIFY_NAME is enabled for all users, but some administrators may disable the privilege for everyone. For more /// information about account privileges, see Privileges and Authorization Constants. /// /// /// See Forcing a User to Change the Logon Password for a code sample that demonstrates how to force a user to change the logon /// password on the next logon using the NetUserGetInfo and NetUserSetInfo functions. /// /// /// The NetUserChangePassword function does not control how the oldpassword and newpassword parameters are secured when sent /// over the network to a remote server. Any encryption of these parameters is handled by the Remote Procedure Call (RPC) mechanism /// supported by the network redirector that provides the network transport. Encryption is also controlled by the security /// mechanisms supported by the local computer and the security mechanisms supported by remote network server or domain specified in /// the domainname parameter. For more details on security when the Microsoft network redirector is used and the remote network /// server is running Microsoft Windows, see the protocol documentation for MS-RPCE, MS-SAMR, MS-SPNG, and MS-NLMP. /// /// public void ChangePassword(string oldPassword, string newPassword, string domainName = null) => NetUserChangePassword(domainName, UserName, oldPassword, newPassword).ThrowIfFailed(); /// Equalses the specified other. /// The other. /// public bool Equals(UserAccount other) => other?.UserName == UserName && other.Target == Target; internal static uint DTtoDWORD(DateTime? dt) => dt.HasValue ? (uint)(dt.Value.ToUniversalTime() - baseDate).TotalSeconds : uint.MaxValue; internal static DateTime? DWORDtoDT(uint baseSec) => baseSec == 0 || baseSec == uint.MaxValue ? null : (baseDate + TimeSpan.FromSeconds(baseSec)).ToLocalTime(); } /// Represents the collection of user accounts on a server. public class UserAccounts : ICollection { /// Initializes a new instance of the class. /// /// The DNS or NetBIOS name of the remote server on which the user account resides. If this value is , the /// local computer is assumed. /// public UserAccounts(string target = null) => Target = target; /// /// Gets the DNS or NetBIOS name of the remote server on which the user account resides. If this value is , /// the local computer is assumed. /// /// The target. public string Target { get; } /// Gets the number of elements contained in the collection. /// The number of elements contained in the collection. public int Count => Enumerate().Count(); /// Gets a value indicating whether the collection is read only. /// if the collection is read only; otherwise, . bool ICollection.IsReadOnly => false; /// Adds a user account and assigns a password and other key properties. /// The name of the user account. /// The password of the user. The length cannot exceed PWLEN bytes. /// The optional path of the home directory for the user. /// An optional comment to associate with the user account. /// The script path. /// Flags that describe the account type and options. /// On success, returns an instance of for the specified user. /// /// /// If you call this function on a domain controller that is running Active Directory, access is allowed or denied based on the /// access control list (ACL) for the securable object. The default ACL permits only Domain Admins and Account Operators to call /// this function. On a member server or workstation, only Administrators and Power Users can call this function. For more /// information, see Security Requirements for the Network Management Functions. For more information on ACLs, ACEs, and access /// tokens, see Access Control Model. /// /// /// The security descriptor of the user container is used to perform the access check for this function. The caller must be able to /// create child objects of the user class. /// /// /// Server users must use a system in which the server creates a system account for the new user. The creation of this account is /// controlled by several parameters in the server's LanMan.ini file. /// /// If the newly added user already exists as a system user, the parameter is ignored. /// /// When you call the AddNew function, the call initializes the additional members in the object to /// their default values. You can change the default values by setting properties on the object. /// /// /// User account names are limited to 20 characters and group names are limited to 256 characters. In addition, account names cannot /// be terminated by a period and they cannot include commas or any of the following printable characters: ", /, , [, ], :, |, <, /// >, +, =, ;, ?, *. Names also cannot include characters in the range 1-31, which are non-printable. /// /// public UserAccount Add(string userName, string password, string homeFolder = null, string comment = null, string scriptPath = null, UserAcctCtrlFlags flags = 0) { flags |= UserAcctCtrlFlags.UF_SCRIPT; NetUserAdd(Target, new USER_INFO_1 { usri1_name = userName, usri1_password = password, usri1_home_dir = homeFolder, usri1_comment = comment, usri1_script_path = scriptPath, usri1_flags = flags, usri1_priv = UserPrivilege.USER_PRIV_USER }, 1); return new UserAccount(userName, Target); } /// Removes all items from the collection. public void Clear() { foreach (UserAccount a in Enumerate().ToArray()) { Remove(a); } } /// Determines whether the collection contains the specified user account. /// The user account to find. /// if the collection contains the specified user account, otherwise, . public bool Contains(UserAccount item) => Enumerate().Contains(item); /// Copies the elements of the collection to an Array, starting at a particular Array index. /// /// The one-dimensional Array that is the destination of the elements copied from the collection. The Array must have zero-based indexing. /// /// The zero-based index in at which copying begins. public void CopyTo(UserAccount[] array, int arrayIndex) { UserAccount[] a = Enumerate().ToArray(); Array.Copy(a, 0, array, arrayIndex, a.Length); } /// Returns an enumerator that iterates through the collection. /// An enumerator that can be used to iterate through the collection. public IEnumerator GetEnumerator() => Enumerate().GetEnumerator(); /// Deletes this account from the server. /// /// /// If you call this function on a domain controller that is running Active Directory, access is allowed or denied based on the /// access control list (ACL) for the securable object. The default ACL permits only Domain Admins and Account Operators to call /// this function. On a member server or workstation, only Administrators and Power Users can call this function. For more /// information, see Security Requirements for the Network Management Functions. For more information on ACLs, ACEs, and access /// tokens, see Access Control Model. /// /// The security descriptor of the User object is used to perform the access check for this function. /// /// An account cannot be deleted while a user or application is accessing a server resource. If the user was added to the system /// with a call to the NetUserAdd function, deleting the user also deletes the user's system account. /// /// public bool Remove(UserAccount user) => NetUserDel(user.Target, user.UserName).Succeeded; /// void ICollection.Add(UserAccount item) => throw new NotImplementedException(); /// Returns an enumerator that iterates through the collection. /// An enumerator that can be used to iterate through the collection. IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); private IEnumerable Enumerate() => NetUserEnum(Target, 0).Select(u => new UserAccount(u.usri0_name, Target)); } }