Every member computer in an Active Directory domain establishes a secure channel with a domain controller. The computer’s password is stored locally in the form of an LSA secret and in Active Directory. This password is used by the NetLogon service to establish the secure channel with a domain controller. If for some reason the LSA secret and computer password become out of sync, the computer will no longer be able to authenticate in the domain. The nltest /sc_query command can query a computer to verify its secure channel is working. Here is sample output from the command when things are working:
Flags: 30 HAS_IP HAS_TIMESERV Trusted DC Name \\dc1.rallencorp.com Trusted DC Connection Status Status = 0 0x0 NERR_Success The command completed successfully
If a secure channel is failing, you’ll need to reset the computer as described in Recipe 8.9. Here is sample output when things are not working:
Flags: 0 Trusted DC Name Trusted DC Connection Status Status = 1311 0x51f ERROR_NO_LOGON_SERVERS The command completed successfully
See Also
Recipe 8.9 for resetting a computer and MS KB 216393 (Resetting Computer Accounts in Windows 2000 and Windows XP)
You want to reset a computer because its secure channel is failing.
Solution
Using a graphical user interface
Open the ADUC snap-in.
If you need to change domains, right-click on Active Directory Users and Computers in the left pane, select “Connect to Domain,” enter the domain name, and click OK.
In the left pane, right-click on the domain and select Find.
Beside Find, select Computers.
Type the name of the computer and click Find Now.
In the Search Results, right-click on the computer and select Reset Account.
Click Yes to verify.
Click OK.
Rejoin the computer to the domain.
Using a command-line interface
You can use the DSMod utility to reset a computer’s password. You will need to rejoin the computer to the domain after doing this.
> dsmod computer "<ComputerDN>" -reset
Another option is to use thenetdomcommand, which can reset the secure channel between the computer and the domain controller without affecting the computer’s password, so that you do not need to rejoin it to the domain:
You can also use thenltest command to reset a secure channel using the following syntax:
> nltest /sc_reset:<DomainName>\<DCName>
Using VBScript
' This resets an existing computer object's password to initial default. ' You'll need to rejoin the computer after doing this. set objComputer = GetObject(LDAP://<ComputerDN>") objComputer.SetPassword "<ComputerName>"
Discussion
When you’ve identified that a computer’s secure channel has failed, you’ll need to reset the computer object, which consists of setting the computer object password to the name of the computer. This is the default initial password for new computers. Every 30 days, Windows 2000 and newer systems automatically change their passwords in the domain. After you’ve set the password, you’ll need to rejoin the computer to the domain since it will no longer be able to communicate with a domain controller due to unsynchronized passwords. However, the netdom reset command will try to reset the password on both the computer and in Active Directory, which will not necessitate rejoining it to the domain if successful.
From a practical standpoint, you should first attempt to reset the secure channel between the computer and the domain using thenetdomornltestsyntaxes, since doing so will not require you to unjoin and rejoin the computer to the domain; in particular, this will save you from performing the associated reboots involved with rejoining the domain. If resetting the secure channel does not correct the issue you’re facing, you can then resort to resetting the computer’s password.
See Also
Recipe 8.3 for joining a computer to a domain, Recipe 8.8 for testing a secure channel, MS KB 216393 (Resetting Computer Accounts in Windows 2000 and Windows XP), and MS KB 325850 (How to Use Netdom.exe to Reset Machine Account Passwords of a Windows Server 2003 Domain Controller)
You want to find inactive computer accounts in a domain.
Solution
These solutions might only apply to Windows-based machines. Other types of machines—e.g., Unix, Mac, Network Attached Storage (NAS)—that have accounts in Active Directory might not update their login timestamps or passwords, which are used to determine inactivity.
Using a command-line interface
The following query will locate all inactive computers in the current forest:
These commands can only be run against a Windows Server 2003 domain functional level or higher domain.
You can also use the OldCmp joeware utility to create a report of all computer accounts whose passwords are older than a certain number of days (90 by default) by using the following syntax:
> oldcmp -report
To specify an alternate password age witholdcmp, use the–age x switch. You can also use the–lltsswitch to use thelastLogonTimeStampattribute to perform the age calculations. (Without this switch,oldcmpwill usepwdLastSetby default.)
Using Perl
#!perl
#---------------------- # Script Configuration #---------------------- # Domain and container/OU to check for inactive computer accounts my $domain = 'amer.rallencorp.com';
# set to empty string to query entire domain my $computer_cont = 'cn=Computers,'; # Number of weeks used to find inactive computers my $weeks_ago = 30; #---------------------- # End Configuration #----------------------
use strict; use Win32::OLE; $Win32::OLE::Warn = 3; use Math::BigInt;
# Must convert the number of seconds since $weeks_ago # to a large integer for comparison against lastLogonTimestamp my $sixmonth_secs = time - 60*60*24*7*$weeks_ago; my $intObj = Math::BigInt->new($sixmonth_secs); $intObj = Math::BigInt->new($intObj->bmul('10 000 000')); my $sixmonth_int = Math::BigInt->new( $intObj->badd('116 444 736 000 000 000')); $sixmonth_int =~ s/^[+-]//;
# Set up the ADO connections my $connObj = Win32::OLE->new('ADODB.Connection'); $connObj->{Provider} = "ADsDSOObject"; $connObj->Open; my $commObj = Win32::OLE->new('ADODB.Command'); $commObj->{ActiveConnection} = $connObj; $commObj->Properties->{'Page Size'} = 1000;
# Grab the default root domain name my $rootDSE = Win32::OLE->GetObject(LDAP://$domain/RootDSE); my $rootNC = $rootDSE->Get("defaultNamingContext");
# Run ADO query and print results my $query = "<LDAP://$domain/$computer_cont$rootNC>;"; $query .= "(&(objectclass=computer)"; $query .= "(objectcategory=computer)"; $query .= "(lastlogontimestamp<=$sixmonth_int));"; $query .= "cn,distinguishedName;"; $query .= "subtree"; $commObj->{CommandText} = $query; my $resObj = $commObj->Execute($query); die "Could not query $domain: ",$Win32::OLE::LastError,"\n" unless ref $resObj;
print "\nComputers that have been inactive for $weeks_ago weeks or more:\n"; my $total = 0; while (!($resObj->EOF)) { my $cn = $resObj->Fields(0)->value; print "\t",$resObj->Fields("distinguishedName")->value,"\n"; $total++; $resObj->MoveNext; } print "Total: $total\n";
Discussion
Using a command-line interface
The dsquery computer command is very handy for finding inactive computers that have not logged in to the domain for a number of weeks or months. You can pipe the results of the query to the dsrm command-line utility if you want to remove the inactive computer objects from Active Directory in a single command. Here is an example that would delete all computers in the current domain that have been inactive for 12 weeks or longer:
> for /F "usebackq" %i in (`dsquery computer domainroot -inactive 12`) do dsrm %i
You can also use OldCmp to disable inactive accounts, and then either delete them or move them to an alternate OU. OldCmp has a number of safeties built into the utility to prevent you from deleting a large number of computer accounts without meaning to. For example, OldCmp will not delete an account unless it has first been disabled, it will not modify more than 10 objects at a time unless you manually specify a higher limit, and it simply will not do anything at all to a domain controller computer account under any circumstances. Unless you have a requirement for quickly removing unusedcomputer objects, we’d recommend allowing them to remain inactive for at least three months before removing them. If you don’t really care when the objects get removed, use a year (i.e., 52 weeks) to be on the safe side.
Using Perl
With Windows 2000 Active Directory, the only way you can determine if a computer is inactive is to query either the pwdLastSet or lastLogon attributes. The pwdLastSet attribute is a 64-bit integer that translates into the date and time the computer last updated its password. Since computers are supposed to change their password every 30 days, you could run a query that finds the computers that have not changed their password in several months. This is difficult with VBScript because it does not handle 64-bit integer manipulation very well. There are third-party add-ons that provide 64-bit functions, but none of the built-in VBScript functions can do it and it is nontrivial to implement without an add-on.
ThelastLogonattribute can also be used to find inactive computers because that attribute contains a 64-bit integer representing the last time the computer logged into the domain. The problem with thelastLogonattribute is that it is not replicated. Since it is not replicated, you have to query every domain controller in the domain to find the most recentlastLogonvalue. As you can imagine, this is less than ideal, especially if you have a lot of domain controllers.
Fortunately, in Windows Server 2003, Microsoft added a new attribute calledlastLogonTimestamptouserandcomputerobjects. This attribute contains the approximate last logon timestamp (again in a 64-bit, large-integer format) for the user or computer and is replicated to all domain controllers. It is the approximate last logon because the domain controllers will update the value only if it hasn’t been updated for a certain period of time (such as a week). This prevents the attribute from being updated constantly and causing a lot of unnecessary replication traffic.
Since VBScript was out of the question, we turned to our first love—Perl. It is very rare to find a problem that you can’t solve with Perl and this is no exception. The biggest issue is manipulating a number to a 64-bit integer, which we can do with theMath::BigIntmodule.
First, we determine the time in seconds from 1970 for the date that we want to query computer inactivity against. That is, take the current time and subtract the number of weeks we want to go back. Then we have to convert that number to a big integer. The last step is simply to perform an ADO query for all computers that have alastLogonTimestampless than or equal to the value we just calculated.
See Also
Recipe 6.31 for finding users whose accounts are about to expire
You want to grant users the ability to join more or fewer than 10 computers to a domain. This limit is called the machine account quota.
Solution
Using a graphical user interface
Open the ADSI Edit MMC snap-in and connect to the Domain Naming Context.
Right-click on the domainDNS object for the domain you want to change and select Properties.
Edit the ms-DS-MachineAccountQuota attribute and enter the new quota value.
Click OK twice.
Using a command-line interface
In the following LDIF code replace <DomainDN> with the distinguished name of the domain you want to change and replace <Quota> with the new machine account quota:
' This code sets the machine account quota for a domain. ' ------ SCRIPT CONFIGURATION ------ intQuota = <Quota> strDomain = "<DomainDNSName>" ' e.g. emea.rallencorp.com ' ------ END CONFIGURATION ---------
set objRootDSE = GetObject("LDAP://" & strDomain & "/RootDSE") set objDomain = GetObject("LDAP://" & objRootDSE.Get("defaultNamingContext")) objDomain.Put "ms-DS-MachineAccountQuota", intQuota objDomain.SetInfo WScript.Echo "Updated user quota to " & intQuota
Discussion
In a default Active Directory installation, members of the Authenticated Users group can add and join up to 10 computer accounts in the default Computers container. The number of computer accounts that can be created is defined in the ms-DS-MachineAccountQuota attribute on the domainDNS object for a domain. The default setting is artificially set to 10, but you can easily change that to whatever number you want, including 0, via the methods described in the Solution section. If you set it to 0, users have to be granted explicit permissions in Active Directory to join computers; refer to Recipe 8.3 for instructions on granting these permissions.
Another method for granting users the right to addcomputerobjects, although not recommended, is via Group Policy. If you grant the “Add workstation to domain” right via Computer Configuration -> Windows Settings -> Security Settings -> Local Policies -> User Rights Assignment on a GPO that’s been linked to the Domain Controllers OU, then users will be able to create computer accounts even if they do not have create child permissions on the defaultComputerscontainer. This is a holdover from Windows NT to maintain backward compatibility and should not be used unless absolutely necessary. In fact, a good security best practice would be to remove this user right from any user or group objects that do not require it.
See Also
Recipe 8.3 for permissions needed to join computers to a domain, MS KB 251335 (Domain Users Cannot Join Workstation or Server to a Domain), and MS KB 314462 (“You Have Exceeded the Maximum Number of Computer Accounts” Error Message When You Try to Join a Windows XP Computer to a Windows 2000 Domain).
Please check back next week for the conclusion to this article.