Active Directory - check if password never expires?
Solution 1
public bool isPasswordExpired(String p_UserName, String p_DomainName)
{
bool m_Check=false;
int m_Val1 = (int)de1.Properties["userAccountControl"].Value;
int m_Val2 = (int) 0x10000;
if (Convert.ToBoolean(m_Val1 & m_Val2))
{
m_Check = true;
} //end
return m_Check
}
Solution 2
If you're on .NET 3.5 and up, you should check out the System.DirectoryServices.AccountManagement
(S.DS.AM) namespace. Read all about it here:
- Managing Directory Security Principals in the .NET Framework 3.5
- MSDN docs on System.DirectoryServices.AccountManagement
Basically, you can define a domain context and easily find users and/or groups in AD:
// set up domain context
PrincipalContext ctx = new PrincipalContext(ContextType.Domain);
// find a user
UserPrincipal user = UserPrincipal.FindByIdentity(ctx, "SomeUserName");
if(user != null)
{
// one of the properties exposed is "PasswordNeverExpires" (bool)
if(user.PasswordNeverExpires)
{
// do something here....
...
}
}
The new S.DS.AM makes it really easy to play around with users and groups in AD!
Solution 3
For .NET 2.0 you can use some LDAP. The magic part is userAccountControl:1.2.840.113556.1.4.803:=65536
. The first part is the property that you want to search, the second means "bitwise AND" and the third is the bitwise flag to check, in this case the 17th bit. You can see more on the bitwise AND and OR in Active Directory in How to query Active Directory by using a bitwise filter.
In the code below update the SearchRoot
variable with your domain controller (DC) and FQDN.
Imports System.DirectoryServices
Public Class Form1
Private Sub Form1_Load(sender As System.Object, e As System.EventArgs) Handles MyBase.Load
''//Bind to the root of our domain
''// YOU_DOMAIN_CONTROLLER should be one of your DCs
''// EXAMPLE and COM are the parts of your FQDN
Dim SearchRoot As DirectoryEntry = New DirectoryEntry("LDAP://YOUR_DOMAIN_CONTROLLER/dc=EXAMPLE,dc=COM")
''//Create a searcher bound to the root
Dim Searcher As DirectorySearcher = New DirectorySearcher(SearchRoot)
''//Set our filer. The last part is dumb but that is the way that LDAP was built.
''//It basically does a bitwise AND looking for the 17th bit to be set on that property "userAccountControl" which is the "password never expires" bit
''//See this if you care to learn more http://support.microsoft.com/kb/269181
Searcher.Filter = "(&(objectCategory=person)(objectClass=user)(userAccountControl:1.2.840.113556.1.4.803:=65536))"
''//Find all of the results
Dim Results = Searcher.FindAll()
Dim DE As DirectoryEntry
''//Loop through each result
For Each R As SearchResult In Results
''//Get the result as a DirectoryEntry object
DE = R.GetDirectoryEntry()
''//Output the object name
Console.WriteLine(DE.Name)
Next
End Sub
End Class
Solution 4
You could use the following code originally from here that I've translated from C# and modified a little bit according to your question (added a getter):
Dim pwdNeverExpires = getPasswordNeverExpires("Tim")
setPasswordNeverExpires("Tim", True)
' See http://msdn.microsoft.com/en-us/library/aa772300(VS.85).aspx
<Flags()> _
Private Enum ADS_USER_FLAG_ENUM
ADS_UF_SCRIPT = 1
' 0x1
ADS_UF_ACCOUNTDISABLE = 2
' 0x2
ADS_UF_HOMEDIR_REQUIRED = 8
' 0x8
ADS_UF_LOCKOUT = 16
' 0x10
ADS_UF_PASSWD_NOTREQD = 32
' 0x20
ADS_UF_PASSWD_CANT_CHANGE = 64
' 0x40
ADS_UF_ENCRYPTED_TEXT_PASSWORD_ALLOWED = 128
' 0x80
ADS_UF_TEMP_DUPLICATE_ACCOUNT = 256
' 0x100
ADS_UF_NORMAL_ACCOUNT = 512
' 0x200
ADS_UF_INTERDOMAIN_TRUST_ACCOUNT = 2048
' 0x800
ADS_UF_WORKSTATION_TRUST_ACCOUNT = 4096
' 0x1000
ADS_UF_SERVER_TRUST_ACCOUNT = 8192
' 0x2000
ADS_UF_DONT_EXPIRE_PASSWD = 65536
' 0x10000
ADS_UF_MNS_LOGON_ACCOUNT = 131072
' 0x20000
ADS_UF_SMARTCARD_REQUIRED = 262144
' 0x40000
ADS_UF_TRUSTED_FOR_DELEGATION = 524288
' 0x80000
ADS_UF_NOT_DELEGATED = 1048576
' 0x100000
ADS_UF_USE_DES_KEY_ONLY = 2097152
' 0x200000
ADS_UF_DONT_REQUIRE_PREAUTH = 4194304
' 0x400000
ADS_UF_PASSWORD_EXPIRED = 8388608
' 0x800000
ADS_UF_TRUSTED_TO_AUTHENTICATE_FOR_DELEGATION = 16777216
' 0x1000000
End Enum
Protected Overridable Function getPasswordNeverExpires(ByVal userName As String) As Boolean
Const userNameString As String = "userName"
Const userFlagsString As String = "userFlags"
Dim machineName As String = Environment.MachineName
Dim userInThisComputerDirectoryEntry As DirectoryEntry = getUserInThisComputerDirectoryEntry(machineName, userName)
If userInThisComputerDirectoryEntry Is Nothing Then
Throw New ArgumentException("not found in " & machineName, userNameString)
End If
Dim userFlagsProperties As PropertyValueCollection = userInThisComputerDirectoryEntry.Properties(userFlagsString)
Dim userFlags As ADS_USER_FLAG_ENUM = CType(userFlagsProperties.Value, ADS_USER_FLAG_ENUM)
Return userFlags = (userFlags Or ADS_USER_FLAG_ENUM.ADS_UF_DONT_EXPIRE_PASSWD)
End Function
Protected Overridable Sub setPasswordNeverExpires(ByVal userName As String, ByVal passwordNeverExpires As Boolean)
Const userNameString As String = "userName"
Const userFlagsString As String = "userFlags"
Dim machineName As String = Environment.MachineName
Dim userInThisComputerDirectoryEntry As DirectoryEntry = getUserInThisComputerDirectoryEntry(machineName, userName)
If userInThisComputerDirectoryEntry Is Nothing Then
Throw New ArgumentException("not found in " & machineName, userNameString)
End If
Dim userFlagsProperties As PropertyValueCollection = userInThisComputerDirectoryEntry.Properties(userFlagsString)
Dim userFlags As ADS_USER_FLAG_ENUM = CType(userFlagsProperties.Value, ADS_USER_FLAG_ENUM)
Dim newUserFlags As ADS_USER_FLAG_ENUM = userFlags
If passwordNeverExpires Then
newUserFlags = newUserFlags Or ADS_USER_FLAG_ENUM.ADS_UF_DONT_EXPIRE_PASSWD
Else
newUserFlags = newUserFlags And (Not ADS_USER_FLAG_ENUM.ADS_UF_DONT_EXPIRE_PASSWD)
End If
userFlagsProperties.Value = newUserFlags
userInThisComputerDirectoryEntry.CommitChanges()
End Sub
Protected Overridable Function getUserInThisComputerDirectoryEntry(ByVal machineName As String, ByVal userName As String) As DirectoryEntry
Dim computerDirectoryEntry As DirectoryEntry = getComputerDirectoryEntry(machineName)
Const userSchemaClassName As String = "user"
Return computerDirectoryEntry.Children.Find(userName, userSchemaClassName)
End Function
Protected Overridable Function getComputerDirectoryEntry(ByVal machineName As String) As DirectoryEntry
'Initiate DirectoryEntry Class To Connect Through WINNT Protocol
' see: http://msdn.microsoft.com/en-us/library/system.directoryservices.directoryentry.path.aspx
Const pathUsingWinNTComputerMask As String = "WinNT://{0},computer"
Dim path As String = String.Format(pathUsingWinNTComputerMask, machineName)
Dim thisComputerDirectoryEntry As New DirectoryEntry(path)
Return thisComputerDirectoryEntry
End Function
You have to add a reference to System.DirectoryServices
. I have tested it on Windows Server 2008 with .NET Framework 4 (it should also work under 2.0) without Active Directory. But check it out yourself and feel free to extend it to get/set other properties as well or connect to other machines (SomeDomain/OtherComputerName
instead of Environment.MachineName
).
Madam Zu Zu
Updated on June 05, 2022Comments
-
Madam Zu Zu almost 2 years
Is there a way in Visual Basic to check if the user's password is set to never expire in Active Directory?
I've found a way to find the last date it was changed, but I can't find the other available options.
Dim de As DirectoryServices.DirectoryEntry = GetUser(uDetails.username) Dim objUser = GetObject(de.Path) If objUser.PasswordLastChanged < DateTime.Now.AddMonths(-3) Then ...
Where can I find a list of all available
objUser
properties? -
Tim Schmelter over 12 yearsThat bites! I've changed this setting on the server to test above methods. That's why today some tasks failed to start with Error Value 2147943730 because i need to renew my password ^^