Tải bản đầy đủ (.pdf) (10 trang)

Active Directory Cookbook for windows server 2003- P21 docx

Bạn đang xem bản rút gọn của tài liệu. Xem và tải ngay bản đầy đủ của tài liệu tại đây (35.2 KB, 10 trang )


211
Table 6-3. ADS_USER_FLAG_ENUM values
Name Value Description
controller.
ADS_UF_DONT_EXPIRE_PASSWD 65536 Password will not expire.
ADS_UF_MNS_LOGON_ACCOUNT 131072 MNS logon account.
ADS_UF_SMARTCARD_REQUIRED 262144 Smart card is required for lo
g
ADS_UF_TRUSTED_FOR_DELEGATION 524288 Allow Kerberos delegation.
ADS_UF_NOT_DELEGATED 1048576
Do not allow Kerberos deleg
ADS_UF_TRUSTED_FOR
_
is enabled.
ADS_UF_USE_DES_KEY_ONLY 2097152 Requires DES encryption fo
r
ADS_UF_DONT_REQUIRE_PREAUTH 4194304
Account does not require Ke
r
preauthentication for logon.
ADS_UF_PASSWORD_EXPIRED 8388608
Read-only flag indicating ac
c
password has expired. Only
u
WinNT provider.
ADS_UF_TRUSTED_TO_AUTHENTICATE_FOR_DELEGATION 16777216 Account is enabled for deleg
a
6.24.4 See Also
Recipe 4.12 for setting a bit-flag attribute and MSDN: ADS_USER_FLAG_ENUM


Recipe 6.25 Setting a User's Account to Expire in the
Future
6.25.1 Problem
You want a user's account to expire at some point in the future.
6.25.2 Solution
6.25.2.1 Using a graphical user interface
1. Open the Active Directory Users and Computers snap-in.
2. In the left pane, right-click on the domain and select Find.
3. Select the appropriate domain beside In.
4. Beside Name, type the name of the user you want to modify and click Find Now.
5. In the Search Results, double-click on the user.

212
6. Click the Account tab.
7. Under Account expires, select the radio button beside End of.
8. Select the date the account should expire.
9. Click OK.
6.25.2.2 Using a command-line interface
Valid values for the -acctexpires flag include a positive number of days in the future when the
account should expire, to expire the account at the end of the day, or "never" to disable account
expiration.
> dsmod user "<UserDN>" -acctexpires <NumDays>
6.25.2.3 Using VBScript
' This code sets the account expiration date for a user.
' SCRIPT CONFIGURATION
strExpireDate = "<Date>" ' e.g. "07/10/2004"
strUserDN = "<UserDN>" ' e.g. cn=rallen,ou=Sales,dc=rallencorp,dc=com
' END CONFIGURATION

set objUser = GetObject("LDAP://" & strUserDN)

objUser.AccountExpirationDate = strExpireDate
objUser.SetInfo
WScript.Echo "Set user " & strUserDN & " to expire on " & strExpireDate

' These two lines would disable account expiration for the user
' objUser.Put "accountExpires", 0
' objUser.SetInfo
6.25.3 Discussion
User accounts can be configured to expire on a certain date. Account expiration is stored in the
accountExpires attribute on a user object. This attribute contains a large integer representation
of the date in which the account expires. If you set this attribute to 0, it disables account
expiration for the user (i.e., the account will never expire). Note that this is different than the
dsmod user command where a value of 0 with -acctexpires will cause the account to expire at
the end of the day. Why does it differ from how the
accountExpires attribute works? Great
question.
6.25.4 See Also
MS KB 318714 (HOW TO: Limit User Logon Time in a Domain in Windows 2000) and MSDN:
Account Expiration

213
Recipe 6.26 Finding Users Whose AccountsAre About
to Expire
6.26.1 Problem
You want to find users whose accounts are about to expire.
6.26.2 Solution
6.26.2.1 Using Perl
# This code finds the user accounts that are about to expire.
# SCRIPT CONFIGURATION
# Domain and container/OU to check for accounts that are about to expire

my $domain = '<DomainDNSName>'; ' e.g. amer.rallencorp.com
my $cont = ''; # set to empty string to query entire domain
# Or set to a relative path in the domain, e.g. cn=Users
# Number of weeks until a user will expire
my $weeks_ago = 4;
# END CONFIGURATION

use strict;
use Win32::OLE;
$Win32::OLE::Warn = 3;
use Math::BigInt;

# Need to convert the number of seconds until $weeks_ago
# to a large integer for comparison against accountExpires
my $future_secs = time + 60*60*24*7*$weeks_ago;
my $intObj = Math::BigInt->new($future_secs);
$intObj = Math::BigInt->new($intObj->bmul('10 000 000'));
my $future_largeint =
Math::BigInt->new($intObj->badd('116 444 736 000 000 000'));
$future_largeint =~ s/^[+-]//;

# Now need to convert the current time into a large integer
$intObj = Math::BigInt->new( time );
$intObj = Math::BigInt->new($intObj->bmul('10 000 000'));
my $current_largeint =
Math::BigInt->new($intObj->badd('116 444 736 000 000 000'));
$current_largeint =~ s/^[+-]//;

# Set up the ADO connections.
my $connObj = Win32::OLE->new('ADODB.Connection');

$connObj->{Provider} = "ADsDSOObject";
# Set these next two if you need to authenticate
# $connObj->Properties->{'User ID'} = '<User>';
# $connObj->Properties->{'Password'} = '<Password>';
$connObj->Open;
my $commObj = Win32::OLE->new('ADODB.Command');
$commObj->{ActiveConnection} = $connObj;
$commObj->Properties->{'Page Size'} = 1000;

# Grab the default domain name.

214
my $rootDSE = Win32::OLE->GetObject("LDAP://$domain/RootDSE");
my $rootNC = $rootDSE->Get("defaultNamingContext");

# Run ADO query and print results.
$cont .= "," if $cont and not $cont =~ /,$/;
my $query = "<LDAP://$domain/$cont$rootNC>;";
$query .= "(&(objectclass=user)";
$query .= "(objectcategory=Person)";
$query .= "(!useraccountcontrol:1.2.840.113556.1.4.803:=2)";
$query .= "(accountExpires<=$future_largeint)";
$query .= "(accountExpires>=$current_largeint)";
$query .= "(!accountExpires=0));";
$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 "\nUsers whose account will expire in $weeks_ago weeks or less:\n";
my $total = 0;
while (!($resObj->EOF)) {
print "\t",$resObj->Fields("distinguishedName")->value,"\n";
$total++;
$resObj->MoveNext;
}
print "Total: $total\n";
6.26.3 Discussion
The code to find expiring user objects is very similar to that of Recipe 6.23 for finding expiring
passwords. The main difference is that instead of querying the pwdLastSet attribute, we need to
query accountExpires. Also, instead of setting accountExpires to a timestamp in the past, as
we did for pwdLastSet, it needs to contain a future timestamp for when accounts will expire.
This makes the logic only slightly different. Let's break down the search filter and review the
other differences.
This part of the filter finds all enabled
user objects:
$query .= "(&(objectclass=user)";
$query .= "(objectcategory=Person)";
$query .= "(!useraccountcontrol:1.2.840.113556.1.4.803:=2)";
This next part finds only the accounts that are going to expire. The second line prevents all
currently expired accounts from being returned.
$query .= "(accountExpires<=$future_largeint)";
$query .= "(accountExpires>=$current_largeint)";
The last part of the filter excludes users that are marked to never expire:
$query .= "(!accountExpires=0));";

215
6.26.4 See Also

Recipe 6.23 for more on large integer manipulation, Recipe 6.25 for setting a user's account to
expire, and MS KB 318714 (HOW TO: Limit User Logon Time in a Domain in Windows 2000)
Recipe 6.26 Finding Users Whose AccountsAre About
to Expire
6.26.1 Problem
You want to find users whose accounts are about to expire.
6.26.2 Solution
6.26.2.1 Using Perl
# This code finds the user accounts that are about to expire.
# SCRIPT CONFIGURATION
# Domain and container/OU to check for accounts that are about to expire
my $domain = '<DomainDNSName>'; ' e.g. amer.rallencorp.com
my $cont = ''; # set to empty string to query entire domain
# Or set to a relative path in the domain, e.g. cn=Users
# Number of weeks until a user will expire
my $weeks_ago = 4;
# END CONFIGURATION

use strict;
use Win32::OLE;
$Win32::OLE::Warn = 3;
use Math::BigInt;

# Need to convert the number of seconds until $weeks_ago
# to a large integer for comparison against accountExpires
my $future_secs = time + 60*60*24*7*$weeks_ago;
my $intObj = Math::BigInt->new($future_secs);
$intObj = Math::BigInt->new($intObj->bmul('10 000 000'));
my $future_largeint =
Math::BigInt->new($intObj->badd('116 444 736 000 000 000'));

$future_largeint =~ s/^[+-]//;

# Now need to convert the current time into a large integer
$intObj = Math::BigInt->new( time );
$intObj = Math::BigInt->new($intObj->bmul('10 000 000'));
my $current_largeint =
Math::BigInt->new($intObj->badd('116 444 736 000 000 000'));
$current_largeint =~ s/^[+-]//;

# Set up the ADO connections.
my $connObj = Win32::OLE->new('ADODB.Connection');
$connObj->{Provider} = "ADsDSOObject";
# Set these next two if you need to authenticate
# $connObj->Properties->{'User ID'} = '<User>';
# $connObj->Properties->{'Password'} = '<Password>';

216
$connObj->Open;
my $commObj = Win32::OLE->new('ADODB.Command');
$commObj->{ActiveConnection} = $connObj;
$commObj->Properties->{'Page Size'} = 1000;

# Grab the default domain name.
my $rootDSE = Win32::OLE->GetObject("LDAP://$domain/RootDSE");
my $rootNC = $rootDSE->Get("defaultNamingContext");

# Run ADO query and print results.
$cont .= "," if $cont and not $cont =~ /,$/;
my $query = "<LDAP://$domain/$cont$rootNC>;";
$query .= "(&(objectclass=user)";

$query .= "(objectcategory=Person)";
$query .= "(!useraccountcontrol:1.2.840.113556.1.4.803:=2)";
$query .= "(accountExpires<=$future_largeint)";
$query .= "(accountExpires>=$current_largeint)";
$query .= "(!accountExpires=0));";
$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 "\nUsers whose account will expire in $weeks_ago weeks or less:\n";
my $total = 0;
while (!($resObj->EOF)) {
print "\t",$resObj->Fields("distinguishedName")->value,"\n";
$total++;
$resObj->MoveNext;
}
print "Total: $total\n";
6.26.3 Discussion
The code to find expiring user objects is very similar to that of Recipe 6.23 for finding expiring
passwords. The main difference is that instead of querying the pwdLastSet attribute, we need to
query accountExpires. Also, instead of setting accountExpires to a timestamp in the past, as
we did for pwdLastSet, it needs to contain a future timestamp for when accounts will expire.
This makes the logic only slightly different. Let's break down the search filter and review the
other differences.
This part of the filter finds all enabled user objects:
$query .= "(&(objectclass=user)";
$query .= "(objectcategory=Person)";

$query .= "(!useraccountcontrol:1.2.840.113556.1.4.803:=2)";
This next part finds only the accounts that are going to expire. The second line prevents all
currently expired accounts from being returned.
$query .= "(accountExpires<=$future_largeint)";

217
$query .= "(accountExpires>=$current_largeint)";
The last part of the filter excludes users that are marked to never expire:
$query .= "(!accountExpires=0));";
6.26.4 See Also
Recipe 6.23 for more on large integer manipulation, Recipe 6.25 for setting a user's account to
expire, and MS KB 318714 (HOW TO: Limit User Logon Time in a Domain in Windows 2000)
Recipe 6.27 Determining a User's Last Logon Time

This recipe requires the Windows Server 2003 forest functional level.

6.27.1 Problem
You want to determine the last time a user logged into a domain.
6.27.2 Solution
6.27.2.1 Using a graphical user interface
If you install the AcctInfo.dll extension to Active Directory Users and Computers, you can view
the last logon timestamp.
1. Open the Active Directory Users and Computers snap-in.
2. In the left pane, right-click on the domain and select Find.
3. Select the appropriate domain beside In.
4. Beside Name, type the name of the user you want to modify and click Find Now.
5. In the Search Results, double-click on the user.
6. Click the Additional Account Info tab.
7. View the value for Last-Logon-Timestamp.


AcctInfo.dll can be downloaded from the Microsoft download site:
/>4E63-8629-B999ADDE0B9E&displaylang=en

6.27.2.2 Using VBScript
' This code prints the last logon timestamp for a user.
' SCRIPT CONFIGURATION
strUserDN = "<UserDN>" ' e.g. cn=rallen,ou=Sales,dc=rallencorp,dc=com
' END CONFIGURATION

218

set objUser = GetObject("LDAP://" & strUserDN)
set objLogon = objUser.Get("lastLogonTimestamp")
intLogonTime = objLogon.HighPart * (2^32) + objLogon.LowPart
intLogonTime = intLogonTime / (60 * 10000000)
intLogonTime = intLogonTime / 1440
WScript.Echo "Approx last logon timestamp: " & intLogonTime + #1/1/1601#
6.27.3 Discussion
Trying to determine when a user last logged on has always been a challenge in the Microsoft
NOS environment. In Windows NT, you could retrieve a user's last logon timestamp from a PDC
or BDC, but this timestamp was the last time the user logged on to the PDC or BDC. That means
in order to determine the actual last logon, you'd have to query every domain controller in the
domain. In large environments, this wasn't practical. With Windows 2000 Active Directory,
things did not improve much. A lastLogon attribute is used to store the last logon timestamp,
but unfortunately, this attribute isn't replicated. So again, to get an accurate picture, you'd have to
query every domain controller in the domain for the user's last logon attribute and keep track of
the most recent one.
Now with Windows Server 2003, we finally have a viable solution. A new attribute was added to
the schema for user objects called lastLogonTimestamp. This attribute is similar to the
lastLogon attribute that was available previously, with two distinct differences. First, and most

importantly, this attribute is replicated. That means when a user logs in, the
lastLogonTimestamp attribute will get populated and then replicate to all domain controllers in
the domain.
The second difference is that since lastLogonTimestamp is replicated, special safeguards
needed to be put in place so that users that logged in repeatedly over a short period of time did
not cause unnecessary replication traffic. For this reason, the lastLogonTimestamp is updated
only if the last update occurred a week or more ago. This means that the lastLogonTimestamp
attribute could be up to a week off in terms of accuracy with a user's actual last logon. Ultimately,
this shouldn't be a problem for most situations because lastLogonTimestamp is intended to
address the common problem where administrators want to run a query and determine which
users have not logged in over the past month or more.
6.27.4 See Also
Recipe 6.28 for finding users that have not logged on recently
Recipe 6.28 Finding Users Who Have Not Logged On
Recently

This recipe requires the Windows Server 2003 domain functional level.


219
6.28.1 Problem
You want to determine which users have not logged on recently.
6.28.2 Solution
6.28.2.1 Using a graphical user interface
1. Open the Active Directory Users and Computers snap-in.
2. In the left pane, right-click on the domain and select Find.
3. Beside Find, select Common Queries.
4. Select the number of days beside Days since last logon.
5. Click the Find Now button.
6.28.2.2 Using a command-line interface

> dsquery user -inactive <NumWeeks>
6.28.2.3 Using Perl
# This code finds the users that have not logged in over a period of time
# SCRIPT CONFIGURATION
# Domain and container/OU to check for inactive accounts
my $domain = '<DomainDNSName>'; # e.g. amer.rallencorp.com
my $cont = 'cn=Users'; # set to empty string to query entire domain
# Or set to a relative path in the domain:
# e.g. cn=Users
# Number of weeks a user needs to be inactive to be returned
my $weeks_ago = <NumWeeks>; # e.g. 4
# END CONFIGURATION

use strict;
use Win32::OLE;
$Win32::OLE::Warn = 3;
use Math::BigInt;

# Need to convert the number of seconds since $weeks_ago
# to a large integer for comparison against lastLogonTimestamp
my $past_secs = time - 60*60*24*7*$weeks_ago;
my $intObj = Math::BigInt->new($past_secs);
$intObj = Math::BigInt->new($intObj->bmul('10 000 000'));
my $past_largeint = Math::BigInt->new(
$intObj->badd('116 444 736 000 000 000'));
$past_largeint =~ s/^[+-]//;

# Setup the ADO connections
my $connObj = Win32::OLE->new('ADODB.Connection');
$connObj->{Provider} = "ADsDSOObject";

# Set these next two if you need to authenticate
# $connObj->Properties->{'User ID'} = '<UserUPNOrDN>';
# $connObj->Properties->{'Password'} = '<Password>';
$connObj->Open;
my $commObj = Win32::OLE->new('ADODB.Command');
$commObj->{ActiveConnection} = $connObj;

220
$commObj->Properties->{'Page Size'} = 1000;

# Grab the default domain name
my $rootDSE = Win32::OLE->GetObject("LDAP://$domain/RootDSE");
my $rootNC = $rootDSE->Get("defaultNamingContext");

# Run ADO query and print results
$cont .= "," if $cont and not $cont =~ /,$/;
my $query = "<LDAP://$domain/$cont$rootNC>;";
$query .= "(&(objectclass=user)";
$query .= "(objectcategory=Person)";
$query .= "(!useraccountcontrol:1.2.840.113556.1.4.803:=2)";
$query .= "(lastlogontimestamp<=$past_largeint));";
$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 "\nUsers 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";
6.28.3 Discussion
As I talked about in Recipe 6.27, in Windows Server 2003 a new attribute on user objects called
lastLogonTimestamp contains the approximate last time the user logged on. Using this to find
the users that have not logged on in a number of weeks is much easier than the option with
Windows 2000, where we would need to query every domain controller in the domain.
The GUI and CLI solutions are straightforward, but the Perl solution is a little more complicated.
The code is very similar to that of Recipe 6.27
, and I suggest reading that if you are curious
about the large integer conversions going on.
6.28.4 See Also
Recipe 6.23 for more on computing large integer timestamps and Recipe 6.27 for more on
finding a user's last logon timestamp
Recipe 6.29 Setting a User's Profile Attributes
6.29.1 Problem
You want to set one or more of the user profile attributes.

×