mirror of https://github.com/tongzx/nt5src
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
527 lines
19 KiB
527 lines
19 KiB
@rem = '
|
|
@goto endofperl
|
|
';
|
|
|
|
$USAGE = "
|
|
Usage: $0 InputFiles
|
|
|
|
MKDSX makes NTDS-Connection objects. An input file specifies where the
|
|
connection is to be created, updated or deleted. Create and update commands
|
|
take additional parameters specifying whether the connection is enabled and
|
|
a schedule parameter. Since connection names are often GUIDs a specific
|
|
connection among a list of alternate connections is located by the value of
|
|
the connection's From-Sever attribute.
|
|
|
|
MKDSX Parameters set via enviroment vars.
|
|
|
|
MKDSX_REDO_FILE Output file of failed mkdsx commands (default: MKDSX.REDO)
|
|
MKDSX_DCNAME computer name of DC to bind to.
|
|
|
|
Input command file entries consist of one record per connection operation.
|
|
The following commands are supported.
|
|
|
|
Host Server From Server Enabled Schedule option
|
|
create site\\server site\\server on|off s-ii-cc-pp [/dc dcname] # comment
|
|
update site\\server site\\server on|off s-ii-cc-pp [/dc dcname] # comment
|
|
del site\\server site\\server|* [/dc dcname] # comment
|
|
dump site\\server site\\server|* [/dc dcname] # comment
|
|
/dc <default computer name of DC on which to create the connection objects>
|
|
/auto_cleanup
|
|
/debug processes the file but prevents mkdsxe.exe from actually modifying the DC.
|
|
/verbose enables verbose output.
|
|
/schedmask <file with 7x24 string of ascii hex digit pairs to turn off the schedule>
|
|
/schedoverride <file with 7x24 string of ascii hex digit pairs to turn on the schedule>
|
|
|
|
No embedded blanks are allowed within parameters.
|
|
|
|
|
|
The /dc option can be used in two ways:
|
|
1. On a command line by itself to specify the global default DC on which
|
|
to create/update the connection object for subsequent connection operation
|
|
commands. The default can be changed multiple times in the input file.
|
|
2. The /dc option can also be used at the end of the create, update, del
|
|
and dump commands to override the current global default for this
|
|
single command. This is useful if you need to create a connection object
|
|
on a remote DC that currently has no connection objects and so is not
|
|
replicating but the global default is sufficient for all the other commands.
|
|
|
|
The /auto_cleanup option is used to automatically delete ALL old connections
|
|
under a given host site\\server before the first new connection is created.
|
|
This is done only once before the first create operation on the host is processed.
|
|
If the first operation on a given host is an update command then it is assumed
|
|
that no cleanup should be done on this host. The del and dump commands do not
|
|
trigger an auto cleanup.
|
|
|
|
Create: Create a connection under the host server in the specified site.
|
|
The DN for the From-Server attribute is built using the specified site
|
|
and server name. The Enabled-Connection attribute is set based on the
|
|
on|off parameter. The Schedule attribute is constructed using the
|
|
schedule paramter. Create behaves like update if the specified connection
|
|
already exists.
|
|
|
|
Update: Update a connection under the host server in the specified site.
|
|
Parameters are the same as for create. The specific connection is found
|
|
by searching for a connection under the host site\\server with a matching
|
|
From-Server attribute. Update returns an error if the specified connection
|
|
is not found.
|
|
|
|
Update reads the schedule and enabled attributes and performs the
|
|
update only if there is a change. This means that the same connection
|
|
data file can be run repeatedly trying to create connections and only
|
|
perform creates or updates as needed.
|
|
|
|
Del : Delete the connection under the host server in the specified site.
|
|
The specific connection is found as described in the update command.
|
|
If there are duplicate connection objects with the same From Server
|
|
attribute then all are deleted.
|
|
|
|
In addition, if '*' is specified for the From Server parameter then
|
|
all NTDS-Connection objects under the host site\\server are deleted.
|
|
|
|
Dump: Dump out the attribute information for the specified connections.
|
|
|
|
|
|
Automatically building a hub-spoke topology -
|
|
|
|
An associated script called mkhubbchtop takes a list of hub servers and a list
|
|
of branch servers and builds a connection data file for a hub-branch topology
|
|
that can be used by this script. mkhubbchtop balances the branch servers
|
|
across the list of hub servers with a staggered schedule so all branches are
|
|
not hitting the hub server at the same time. See the help info in mkhubbchtop
|
|
|
|
|
|
Schedule parameter and load sharing -
|
|
|
|
The schedule parameter provides a means for setting a repeating replication schedule.
|
|
Connection schedules are an attribute associated with each NTDS-Connection object
|
|
in the DC. They contain a 7x24 array of bytes, one byte for each hour in a
|
|
7 day week (UTC time zone). You can use the schedule paramter to spread the
|
|
replication load among multiple inbound source servers.
|
|
|
|
The paramter is of the form s-iii-ccc-ppp where the i, c and p fields are decimal
|
|
numbers. The i field describes the desired interval (in hours) between inbound
|
|
replication events on the host server. The c field describes the number of
|
|
connections objects present that will import data to this host server. Or, put
|
|
another way, it is the number of other servers that will be providing data to
|
|
this server. The p field is offset parameter that ranges from 0 to c-1. It
|
|
is used to stagger the schedule relative to the schedules on the other connection
|
|
objects.
|
|
|
|
For example, lets assume you have two connection objects that refer to two servers
|
|
SA and SB which will supply replication updates to the host server. We would
|
|
like to arrange the schedules of these two connection objects so that our host
|
|
server is updated every 4 hours. To do this, the schedule parameter for the
|
|
connection object referring to server SA would be 's-4-2-0' and the parameter
|
|
for the connection to server SB is 's-4-2-1'.
|
|
|
|
Source H o u r
|
|
Server 0 4 8 12 16 20
|
|
|
|
SA s-4-2-0 1 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 ...
|
|
SB s-4-2-1 0 0 0 0 1 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 1 0 0 0 ...
|
|
|
|
Repl Events ^ ^ ^ ^ ^ ^
|
|
|
|
|
|
With these two connection objects the net interval between replication events
|
|
will be every 4 hours. The schedule for server B is offset by 4 hours as a result
|
|
of the p field being 1. Both of the above schedule patterns continue to repeat
|
|
over the course of the 7x24 array comprising the schedule attribute.
|
|
|
|
As a second example, assume you want to replicate every 2 hours and you establish
|
|
connections with three other servers to provide the data. The schedule paramter
|
|
values and the schedule array are then:
|
|
|
|
Source H o u r
|
|
Server 0 4 8 12 16 20
|
|
|
|
SA s-2-3-0 1 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 0 ...
|
|
SB s-2-3-1 0 0 1 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 ...
|
|
SC s-2-3-2 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 0 1 0 ...
|
|
|
|
Repl Events ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^
|
|
|
|
|
|
Schedmask and Schedoverride parameters -
|
|
|
|
Schedmask and schedoverride data are formatted as a pair of ascii hex digits
|
|
for each byte in the 7x24 schedule array with byte 0 corresponding to day 1 hour 0.
|
|
For each connection the 7x24 result schedule is formed using the schedule parameter (see
|
|
below) and then the schedule mask is applied (each bit set in the schedule mask
|
|
clears the corresponding bit in the result schedule). Finally the schedule override
|
|
is applied with a logical OR of the override schedule to the result schedule.
|
|
Schedmask and schedoverride can have embedded whitespace chars (including cr/lf)
|
|
which are deleted or be a single string of 336 (7*24*2) hex digits. For example:
|
|
|
|
FF0000000000 000000000000 000000000000 000000000000
|
|
FF0000000000 FFFFFFFFFF00 000000000000 000000000000
|
|
FF0000000000 FFFFFFFFFF00 000000000000 000000000000
|
|
FF0000000000 FFFFFFFFFF00 000000000000 000000000000
|
|
FF0000000000 FFFFFFFFFF00 000000000000 000000000000
|
|
FF0000000000 000000000000 000000000000 000000000000
|
|
FF0000000000 000000000000 000000000000 000000000000
|
|
|
|
|
|
Sample Input File -
|
|
|
|
#A sample input file might look as follows:
|
|
#
|
|
/dc ntdev-dc-01
|
|
/clean
|
|
#
|
|
# Host Server From Server Enabled Schedule
|
|
#
|
|
create Red-Bldg40\\ntdev-dc-01 Red-bldg40\\ntdsdc9 on s-4-2-0 # update every 8 hours beg at hr 0
|
|
create Red-Bldg40\\ntdev-dc-01 Red-bldg40\\ntdsdc8 on s-4-2-1 # update every 8 hours beg at hr 4
|
|
|
|
# update every 2 hours beg at hr 0
|
|
update Red-Bldg40\\ntdev-dc-01 Red-bldg40\\ntdsdc90 on s-2-1-0
|
|
|
|
#End of file
|
|
|
|
|
|
Error handling -
|
|
|
|
Any command line that returns an error is written to the ReDo file.
|
|
An error message is written to standard out.
|
|
|
|
Note: The redo file is deleted when the script starts so if no redo file exists
|
|
after completion of the script then all commands were processed without errors.
|
|
|
|
|
|
";
|
|
|
|
|
|
|
|
die $USAGE unless @ARGV;
|
|
|
|
## $mkdsx = "mkdsxe.exe /v ";
|
|
$mkdsx = "mkdsxe.exe ";
|
|
|
|
$time = scalar localtime;
|
|
printf DAT ("Running mkdsx on: %s\n", $time);
|
|
printf("\n\n");
|
|
|
|
$redo = $ENV{'MKDSX_REDO_FILE'}; printf("MKDSX_REDO_FILE: %s\n", $redo);
|
|
$dcname = $ENV{'MKDSX_DCNAME'}; printf("MKDSX_DCNAME: %s\n", $dcname);
|
|
$verbose = $ENV{'MKDSX_VERBOSE'}; printf("MKDSX_VERBOSE: %s\n", $verbose);
|
|
|
|
if ($redo eq "") {$redo = "mkdsx.redo";}
|
|
|
|
if ($dcname ne "") {$dcname = "/dc $dcname";}
|
|
if ($verbose ne "") {$verbosemode = "/v";}
|
|
|
|
printf("\n\n");
|
|
print $0 @argv;
|
|
|
|
printf("Redo File: %s\n", $redo) if ($redo ne "");
|
|
|
|
|
|
#
|
|
# mkdsx.exe error return codes
|
|
#
|
|
$ErrMsg[0] = "Success.";
|
|
$ErrMsg[1 ] = "Invalid Arguments.";
|
|
$ErrMsg[2 ] = "Could not bind to the DC.";
|
|
$ErrMsg[3 ] = "Could not find 'NTDS Settings' object. Check the host site\\server parameter.";
|
|
$ErrMsg[4 ] = "Could not find 'NTDS Settings' object. Check the from site\\server parameter.";
|
|
$ErrMsg[5 ] = "Error creating connection.";
|
|
$ErrMsg[6 ] = "Connection already exists.";
|
|
$ErrMsg[7 ] = "Error updating connection.";
|
|
$ErrMsg[8 ] = "Error updating connection; connection not found.";
|
|
$ErrMsg[9 ] = "Error updating connection; duplicate connections found.";
|
|
$ErrMsg[10] = "Error deleting connection.";
|
|
$ErrMsg[11] = "Error deleting connection; connection not found.";
|
|
$ErrMsg[12] = "Deleting multiple connection.";
|
|
$ErrMsg[13] = "Error dumping connection.";
|
|
$ErrMsg[14] = "Error dumping; connection not found.";
|
|
$ErrMsg[15] = "Dumping duplicate connections.";
|
|
|
|
#
|
|
# Valid commands with number of required params.
|
|
#
|
|
$cmdtab{"/dc"} = 1;
|
|
$cmdtab{"/schedmask"} = 1;
|
|
$cmdtab{"/schedoverride"} = 1;
|
|
$cmdtab{"/debug"} = 0;
|
|
$cmdtab{"/verbose"} = 0;
|
|
$cmdtab{"/auto_cleanup"} = 0;
|
|
$cmdtab{"create"} = 4;
|
|
$cmdtab{"update"} = 4;
|
|
$cmdtab{"del"} = 2;
|
|
$cmdtab{"dump"} = 2;
|
|
|
|
$linenumber = 0;
|
|
$InFile = "";
|
|
|
|
unlink $redo;
|
|
$redo_cnt = 0;
|
|
$cleanup = 0;
|
|
|
|
while (<>) {
|
|
|
|
if ($InFile ne $ARGV) {
|
|
$InFile = $ARGV;
|
|
printf("Processing file %s \n\n", $InFile);
|
|
$linenumber = 0;
|
|
}
|
|
$linenumber++;
|
|
$cleancmd = "";
|
|
|
|
chop;
|
|
|
|
($func, @a) = split;
|
|
if (($func eq "") || ($func =~ m/^#/)) {next;}
|
|
|
|
#
|
|
# check for valid command and for missing or extraneous parameters.
|
|
#
|
|
$func = lc($func);
|
|
|
|
if (!exists($cmdtab{$func})) {
|
|
printf("Line %d: Error: %s unrecognized command.\n%s\n\n", $linenumber, $func, $_);
|
|
goto ERROR;
|
|
}
|
|
|
|
$numargs = $cmdtab{$func};
|
|
|
|
#
|
|
# Are there any optional params present?
|
|
#
|
|
$option = ""; $optionarg = "";
|
|
if ($a[$numargs] =~ m/\/dc/i) {
|
|
$option = $a[$numargs]; $optionarg = $a[$numargs+1];
|
|
$numargs += 2;
|
|
}
|
|
|
|
if (($a[$numargs] ne "") && !($a[$numargs] =~ m/^#/)) {
|
|
printf("Line %d: Error: %s has extraneous or missing parameters - skipping\n%s\n\n", $linenumber, $func, $_);
|
|
goto ERROR;
|
|
}
|
|
|
|
$i = $numargs;
|
|
while ($i-- > 0) {
|
|
if (($a[i] eq "") || ($a[$i] =~ m/^#/)) {
|
|
printf("Line %d: Error: %s missing parameters - skipping\n%s\n\n", $linenumber, $func, $_);
|
|
goto ERROR;
|
|
}
|
|
}
|
|
|
|
#
|
|
# func a0 a1 a2 a3 a4
|
|
# Host Server From Server Enabled Schedule option
|
|
# create site\\server site\\server on|off s-ii-cc-pp [/dc dcname] # comment
|
|
# update site\\server site\\server on|off s-ii-cc-pp [/dc dcname] # comment
|
|
#
|
|
# /dc <default computer name of DC on which to create the connection objects>
|
|
# /auto_cleanup
|
|
# /debug
|
|
# /schedmask <file>
|
|
# /schedoverride <file>
|
|
|
|
if ($func =~ m/\/dc/i) {
|
|
$dcname = "/dc $a[0]";
|
|
printf("Default DC name change: %s\n", $a[0]);
|
|
next;
|
|
}
|
|
|
|
if ($func =~ m/\/schedmask/i) {
|
|
$schedmask = "/schedmask $a[0]";
|
|
printf("schedmask change: %s\n", $a[0]);
|
|
next;
|
|
}
|
|
|
|
if ($func =~ m/\/schedoverride/i) {
|
|
$schedoverride = "/schedoverride $a[0]";
|
|
printf("schedoverride change: %s\n", $a[0]);
|
|
next;
|
|
}
|
|
|
|
if ($func =~ m/\/auto_cleanup/i) {
|
|
printf("Automatic cleanup (i.e. delete) of old connections under each site\server is enabled.\n");
|
|
$cleanup = 1;
|
|
next;
|
|
}
|
|
|
|
if ($func =~ m/\/debug/i) {
|
|
printf("Debug mode enabled. DC modifications supressed.\n");
|
|
$debugmode = "/debug";
|
|
next;
|
|
}
|
|
|
|
if ($func =~ m/\/verbose/i) {
|
|
printf("Verbose mode enabled.\n");
|
|
$verbosemode = "/v";
|
|
next;
|
|
}
|
|
|
|
$host = $a[0]; $fromsrv = $a[1]; $enable = $a[2]; $sched = $a[3];
|
|
|
|
($host_site, $host_srv) = split(/\\/, $host);
|
|
($from_site, $from_srv) = split(/\\/, $fromsrv);
|
|
|
|
#
|
|
# check for correct number of parameters.
|
|
#
|
|
if (($host_site eq "") || ($host_srv eq "")) {
|
|
printf("Line %d: Error: Host Site or Server is null - skipping\n%s\n\n", $linenumber, $_);
|
|
goto ERROR;
|
|
}
|
|
|
|
if (($from_site eq "") || (($from_srv eq "") && ($from_site ne "*"))) {
|
|
printf("Line %d: Error: Host Site or Server is null - skipping\n%s\n\n", $linenumber, $_);
|
|
goto ERROR;
|
|
}
|
|
|
|
#
|
|
# check for DC override option.
|
|
#
|
|
$binddc = $dcname;
|
|
if ($option =~ m/\/dc/i) {
|
|
$binddc = "/dc $optionarg";
|
|
}
|
|
|
|
#
|
|
# build function call for create / update commands.
|
|
#
|
|
if ($func =~ m/create|update/i) {
|
|
|
|
if ($from_site eq "*") {
|
|
printf("Line %d: From site of \"*\" not allowed for %s command - skipping\n%s\n\n",
|
|
$linenumber, $func, $_);
|
|
goto ERROR;
|
|
}
|
|
|
|
if (!($enable =~ m/on|off/i)) {
|
|
printf("Line %d: Third parameter of %s command must be 'on' or 'off' - skipping\n%s\n\n",
|
|
$linenumber, $func, $_);
|
|
goto ERROR;
|
|
}
|
|
$enable_arg = ($enable =~ m/on/i) ? "/enable" : "/disable";
|
|
|
|
if (!($sched =~ m/s\-[0-9]+\-[0-9]+\-[0-9]+/i)) {
|
|
printf("Line %d: Fourth parameter of %s command must be s-iii-ccc-ppp - skipping\n%s\n\n",
|
|
$linenumber, $func, $_);
|
|
goto ERROR;
|
|
}
|
|
($junk, $iii, $ccc, $ppp) = split(/-/, $sched);
|
|
|
|
if ($ppp >= $ccc) {
|
|
printf("Line %d: Fourth parameter of %s command s-iii-ccc-ppp, ppp must be less than ccc - skipping\n%s\n\n",
|
|
$linenumber, $func, $_);
|
|
goto ERROR;
|
|
}
|
|
|
|
#
|
|
# Make up a delete command if we are auto cleaning old connections.
|
|
#
|
|
if ($cleanup && ($func =~ m/create/i) && !$hostcleaned{$host}) {
|
|
$cleancmd = "$mkdsx $binddc $debugmode $verbosemode \
|
|
/del /tosite $host_site /toserver $host_srv /all";
|
|
}
|
|
|
|
#
|
|
# Remember that we have done either a create or update against this
|
|
# host site\server so it is done only once. In particular if we
|
|
# see an update command for a host before the first create for the host
|
|
# we will NOT do a cleanup on that host if we see a create for it later.
|
|
#
|
|
$hostcleaned{$host} += 1;
|
|
|
|
#
|
|
# Make a cxtion name if a create.
|
|
#
|
|
$name_arg = ($func =~ m/create/i) ? "/name From-$from_site-$from_srv" : "";
|
|
|
|
$mcmd = "$mkdsx $binddc $debugmode $verbosemode /$func $name_arg $enable_arg \
|
|
/tosite $host_site /toserver $host_srv \
|
|
/fromsite $from_site /fromserver $from_srv \
|
|
/schedule $iii $ccc $ppp $schedmask $schedoverride";
|
|
}
|
|
|
|
#
|
|
# build function calls for del / dump commands.
|
|
#
|
|
# func a0 a1 a2 a3
|
|
# del site\\server site\\server|* [/dc dcname] # comment
|
|
# dump site\\server site\\server|* [/dc dcname] # comment
|
|
#
|
|
if ($func =~ m/del|dump/i) {
|
|
|
|
$fromarg = ($from_site eq "*") ? "/all" : "/fromsite $from_site /fromserver $from_srv";
|
|
|
|
$mcmd = "$mkdsx $binddc $debugmode $verbosemode /$func \
|
|
/tosite $host_site /toserver $host_srv $fromarg ";
|
|
}
|
|
|
|
#
|
|
# Do the operation on the connection.
|
|
#
|
|
if ($verbosemode ne "" ) {printf("\n");}
|
|
printf("%s\n", $_);
|
|
|
|
if ($cleancmd ne "") {
|
|
#
|
|
# Do the cleanup command first to delete old connections on this site\server.
|
|
#
|
|
if ($verbosemode ne "") {printf("\nRunning autoclean:\n%s\n", $cleancmd)};
|
|
$rc = system ($cleancmd) / 256;
|
|
if ($rc != 0) {
|
|
printf("Line %d: Status return from mkdsx.exe (%d) - %s - for auto cleanup command. Continuing.\n",
|
|
$linenumber, $rc, $ErrMsg[$rc]);
|
|
}
|
|
}
|
|
|
|
if ($verbosemode ne "" ) {printf("\nRunning:\n%s\n", $mcmd)};
|
|
|
|
$rc = system ($mcmd) / 256;
|
|
if ($rc != 0) {
|
|
printf("Line %d: Error from mkdsx.exe (%d) - %s - skipping\n%s\n\n",
|
|
$linenumber, $rc, $ErrMsg[$rc], $_);
|
|
++$redo_cnt;
|
|
goto REDO_CMD;
|
|
}
|
|
|
|
next;
|
|
|
|
ERROR:
|
|
#
|
|
# append command record to redo file.
|
|
#
|
|
$errorcount += 1;
|
|
|
|
REDO_CMD:
|
|
open(REDO, ">>$redo");
|
|
#
|
|
# put options out first.
|
|
#
|
|
if ($redo_cnt == 1) {
|
|
$time = scalar localtime;
|
|
printf REDO ("#Time generated: %s\n", $time);
|
|
print REDO "$dcname \n";
|
|
print REDO "/auto_cleanup \n" if ($cleanup == 1);
|
|
print REDO "/debug \n" if ($debugmode ne "");
|
|
print REDO "/verbose \n" if ($verbosemode ne "");
|
|
print REDO "$schedmask\n" if ($schedmask ne "");
|
|
print REDO "$schedoverride\n" if ($schedoverride ne "");
|
|
|
|
print REDO "# \n";
|
|
}
|
|
|
|
print REDO "$_\n";
|
|
close(REDO);
|
|
|
|
} # end while()
|
|
|
|
|
|
printf("WARNING: %d command(s) were invalid and not performed. They were written to the Redo File: %s\n",
|
|
$errorcount, $redo) if ($errorcount > 0);
|
|
|
|
printf("WARNING: %d command(s) failed their connection operation. They were written to the Redo File: %s\n",
|
|
$redo_cnt, $redo) if ($redo_cnt > 0);
|
|
|
|
|
|
|
|
__END__
|
|
:endofperl
|
|
@perl %~dpn0.cmd %*
|