Friday, April 30. 2010
LAN for kids
Digital Engine Software and The Bozeman Tech Ranch are proud to
sponsor the first ever LAN for Kids event to be held Saturday May 22nd
from 4:00 p.m. to 10:00 p.m. at the Homewood Suites in Bozeman. Net
proceeds will go to benefit Thrive and the CAP Mentoring program.
Digital Engine Software will be offering the following hosted games
throughout the evening:
- Quake 3
- Unreal Tournament
- WarCraft 3
- Team Fortress 2
Plus players are free to bring and host any other games they want.
Attendees will be treated to all the pizza and drinks they can handle
and the opportunity to win some amazing auction items. Please join us
for a fun night to benefit a great cause.
Tickets are $20
each ($10 with student I.D.) and space is limited so register soon!
Register online at: http://digitalenginesoftware.com/lan4kids
*All networking, power, tables and chairs will be provided. Please do bring your own computer and monitor.Tuesday, April 27. 2010
identityrangemanagement and read-only tables
As a follow-up to yesterday's problems with exhausted identity ranges, I have come to a realization which I am now adopting as a best practice and I highly recommend for others:
@subscriber_upload_options = 2
), always set @identityrangemanagementoption = 'none'
.
The reasoning behind this advice is that when @identityrangemanagementoption = 'auto'
(the default in Management Studio), identity ranges will be allocated for all subscribers, even though they will never use these ranges because the table article is read-only. Yet, if the table has a small datatype for a primary key (such as tinyint or smallint) it is quite likely that the identity range will be exhausted (with the default identity range being 1000, this doesn't take many subscribers) and when this happens synchronization will cease.
If there is a reason for allocating these ranges to read-only table articles or for leaving automatic identity range mangement enabled, please post it in the comments.
Monday, April 26. 2010
Troubleshooting Failed to allocate new identity range
- The Publisher failed to allocate a new set of identity ranges for the subscription. This can occur when a Publisher or a republishing Subscriber has run out of identity ranges to allocate to its own Subscribers or when an identity column data type does not support an additional identity range allocation. If a republishing Subscriber has run out of identity ranges, synchronize the republishing Subscriber to obtain more identity ranges before restarting the synchronization. If a Publisher runs out of identit (Source: MSSQL_REPL, Error number: MSSQL_REPL-2147199417) Get help: http://help/MSSQL_REPL-2147199417
- Not enough range available to allocate a new range for a subscriber. (Source: MSSQLServer, Error number: 20668) Get help: http://help/20668
- Failed to allocate new identity range. (Source: MSSQLServer, Error number: 21197) Get help: http://help/21197
First, some background: I am using a (very) simple replication topology with a single publisher running SQL Server 2005 and ~10 subscribers running SQL Server 2005 Express (all with the latest service packs and patches, of course). No re-publishing servers, multiple publications, etc. The error occurred not long after I had added 2 new subscribers (which turns out to be quite important).
Note: For replication topologies which do include a re-publishing server, the error may be explained by KB941989, which can be solved by installing Cumulative Update 4 (or later updates including the fixes).
To find the cause of this error message, some technical information about how identity values are managed during replication is needed. A key source of information for table articles with identity columns is the MSmerge_identity_range table. This table contains a row for each range of values assigned to each subscriber for each article as well as an additional row holding the entire range of identity values for the table article (this is the row with is_pub_range = 1). In order to make sense of this table, the artid column can be joined to the sysmergearticles table and subid can be joined to the sysmergesubscriptions table. Take note of the max_used column (which only has a value for pub_range rows); this column holds the end of the maximum range that has been allocated for the article (technically 1-past-the-last, since it holds the max of next_range_end, but conceptually it is the end). When this value is near the end of the pub_range, then the available identity ranges are almost exhausted.
One quick way to determine if the problem is due to exhaustion of the available identity range, is with a query similar to the following:
SELECT name, range_begin, range_end, max_used
FROM MSmerge_identity_range mir
INNER JOIN sysmergearticles sma ON mir.artid = sma.artid
WHERE is_pub_range = 1 AND range_end <= max_used + pub_range
Note: In the (very unusual) case that the subscribers are given larger ranges than the publisher(s), replace pub_range with sub_range in the above query. If this query returns any rows, then the problem has been identified. The table articles listed have exhausted their available identity ranges. If the range_end value is below the maximum value for the identity datatype, then it should be possible to increase this value and solve the problem quite easily (Note: I have not found a documented way to do this "safely" and modifying this table directly is probably dangerous, but it solved the problem for me. YMMV). If the range_end value is the maximum for the datatype, then the underlying datatype will likely need to be changed (or significant compaction of the values will need to be done, somehow) and the article dropped and re-added to the publication.
Other sources that I have come across suggested running the sp_adjustpublisheridentityrange stored procedure. It should be safe to run in any case, although I didn't find it particularly useful.
Another suggestion for similar errors that was suggested on the MSDN Forums is to check the identity value on the publisher and that it is within the range allocated to the publisher (both in MSmerge_identity_range and in the check constraint that replication adds to the table for checking the identity range value).
If all of the above suggestions fail, my best suggestion is to watch the synchronization in SQL Profiler (for server versions where SQL Profiler is not available, AnjLab SQLProfiler is a nice alternative) and to examine some of the replication stored procedures using Management Studio (right click->Modify to see the SQL of the SP) to get ideas for what might be going wrong. In particular, consider examining sp_adjustpublisheridentityrange, MSget_identity_range_info, MSpub_adjust_identity, and similar identity management stored procedures.
Good Luck!
Tuesday, March 30. 2010
Bozeman Fiber Prices
Bresnan - $2,000 per month plus $2,500 setup fee. The rep I spoke with said prices normally run between $85 and $115 per megabit per month depending on location and how much you are buying. He also said they can go up to 1 gigabit connections if needed.
Qwest - $2,750 per month. The Qwest rep said they offer a DS3 connection (45 megabits) for about the same price.
Bridgeband - A rough guess of between $2,000 and $3,000 per month.
LightNex - $1700 per month. LightNex also offers average 10 Mbps burstable to 20 connection for around $1100 per month and average 5 burstable to 20 connection for $765 per month.
Little Apple Tech - I met with one of the Little Apple employees at the home show. He told me that they could provide the bandwidth but that they would probably charge as much (or more) than the other providers because they would have to roll out new hardware.
This little exercise confirmed what I had been suspecting: hosting locally doesn't make financial sense. Even the "premium" hosting services like Rackspace and DreamHost have their highest end dedicated servers for half what we would pay for bandwidth only. However, all of the reps I talked to were really awesome, especially Tom from LightNex who came up to visit us despite knowing full well we were just tire kicking. I certainly hope that one day the market shifts so we can work with at least one of these companies in the future.
Thursday, March 25. 2010
Java PermGen space, memory leaks, and debug mode
Over the last couple of days of development on a Java web application using the Wicket framework, I noticed a peculiar behavior on the server while in debug mode. After several redeployments of the application war file to the Tomcat server (without restarting the server), eventually a page reload would hang and the server would start spewing java.lang.OutOfMemoryError: PermGen space
stack traces to the console. Clearly this is indicating a memory leak somewhere in my code or in the libraries my code relies on and on top of that, this isn't the sort of memory leak usually associated with poor memory management, since this is the PermGen space not the Heap space which is running out. Since PermGen is a special place in memory for ClassLoader objects to reside, its not something that most memory profilers will pick up since it shouldn't ever be a problem for normal code.
At this point, after searching for information on what would cause PermGen space to run out, and reading a bunch of mixed responses (most programmers that have blogged about this before me seem to take the head in the sand approach and just increase the PermGen size so that won't run out as fast), I found these two wonderful blog posts by Frank Kieviet. He's analysis of the situation is very enlightened and helped me understand what was going on behind the scenes.
So, using Sun's VisualVM tool, I started profiling the PermGen space of my local Tomcat install while continually deploying and redeploying the code base. Sure enough, every time I changed the code and eclipse automatically redeployed the code to the Tomcat instance, the PermGen size would increase the next time I requested a page and Tomcat started allocating objects. No matter how long I waited or clicked the garbage collect button, the PermGen space was never reclaimed and eventually I'd hit the memory ceiling and the server would start spewing those familiar OutOfMemoryError messages again.
Having read Frank's excellent analysis of static references, I took a trip through the code base in order to find offending static object references that might be causing the leak. Unfortunately, no where in the code was I creating static references that looked like they would have resulted in a ClassLoader leak (not to say that there aren't, as was pointed out in Frank's article, this is a tricky problem to track down and none of my references looked suspicious). Well, if its not in my code, where is it?
Since I'm using Wicket, I created a skeletal test project that creates a single webpage and dynamically sets a span tag on the page to a hard coded String object. I fired up VisualVM, and ran my same deploy and redeploy test and sure enough the PermGen space starts getting eaten up like clockwork. To be fair, the PermGen space didn't fill up nearly as fast since my test project is only allocating a few classes and they're associated ClassLoader objects every redeploy, but given enough time it did eventually result in the same OutOfMemoryErrors in the console.
"Aha!", I thought, "Wicket's developers have made a grievous mistake, and aren't watching their code for potential ClassLoader leaks". I figured that since I'm using Wicket, I'll just have to live with the knowledge that eventually I'll run out of PermGen space and have to restart my server. Certainly annoying to me as a developer, but its definitely a viable workaround. The only problem will be when this code hits production, and I'll have to be careful to restart the server after each code deployment or run the risk of having the server be brought to its knees from memory problems.
I can't remember what particular Google search netted me this link, but I did eventually find this gem of information:
The JDK's permanent memory behaves differently depending on whether a debugging is enable, i.e. if there is an active agent.
If there is an active agent, the JDK can fail to collect permanent memory in some cases. (Specifically, when some code introspects a class with a primitive array like, byte[] or char[].) This can cause the permanent space to run out, for example, when redeploying .war files.
I had no idea that the JVM's garbage collector behaved differently depending on whether the application is running in debug mode or not. Since I'm constantly running my code in debug mode and using Eclipse's hot fix code replace feature, and every code change redeploys the entire .war archive to the server, resulting in more class loaders on the class path, and another chunk of memory that will never be reclaimed by the server.
Therefore, the moral of the story is while trying to track down memory leaks, do not run your application (especially a web application) in debug mode. It is still true that you can leak PermGen space without your application being run in debug mode, so this isn't an excuse for poor coding practices. Rather, its a cautionary tale of my part of how even a simple thing like running your application in debug mode can mean rather drastically different program performance even outside of the debug instruction overhead.
Saturday, March 20. 2010
WshShell.Exec Considered Harmful Due To Blocking
For the unfamiliar, the Exec method of WshShell (used in Windows scripting) runs an application and provides access to that application's standard streams as TextStream objects. The problem with this method is that the blocking behavior of these streams is not defined (as noted in the comments on the StdOut property) and, more importantly, is impossible to use safely. The problem, familiar to anyone who has dealt with reading and writing to child programs, is that it is very easy to block while attempting to read from (or write to) the child process. What makes this problem worse is that TextStream provides no method for dealing with blocking; there is no way to set non-blocking mode or to check if input is ready to be read or to check if it would be safe to write (at least, none that I am aware of).
As a demonstration of the problem, consider the following applications. First, the child program (written in C):
#include <stdio.h>
int main(void)
{
for (int i=0; i<10; ++i) {
for (int j=0; j<8192; ++j)
fputc('x', stdout);
for (int j=0; j<8192; ++j)
fputc('x', stderr);
}
return 0;
}
var WShell = new ActiveXObject("WScript.Shell");
var wsexec = WShell.Exec(cmd);
var output = "";
var error = "";
// Keep looping until the program exits
while (wsexec.Status == 0) {
while (!wsexec.StdOut.AtEndOfStream) {
output += wsexec.StdOut.Read(1);
}
while (!wsexec.StdErr.AtEndOfStream) {
error += wsexec.StdErr.Read(1);
}
WScript.Sleep(100);
}
WScript.Echo("Output: " + output);
WScript.Echo("Error Output: " + error);
The solution that I came up with (which has its own drawbacks in terms of performance), is to write the program output to files, then read it back in the script once the program has finished. This solves the deadlock problem at the expense of decreasing the performance by requiring the output to be written to disk as an intermediate step (although the OS may not flush it to the physical disk). The code for this solution is presented below:
/** Run a command, in a separate process and retrieve its output.
*
* This is a safer, slower, alternative to WshShell.Exec that supports
* retrieving the output (to stdout and stderr) only after the command
* has completed execution. It does not support writing to the standard
* input of the command. It's only redeeming quality is that it will
* not cause deadlocks due to the blocking behavior of attempting to read
* from StdOut/StdErr.
*
* @param cmd The name/path of the command to run
* @param winstyle The window style (see WshShell.Run) of the command, or null
* @return An object with an exitcode property set to the exit code of the
* command, an output property set to the string of text written by the
* command to stdout, and an errors property with the string of text written
* by the command to stderr.
*/
function run(cmd) {
var tmpdir = FSO.GetSpecialFolder(2 /* TemporaryFolder */);
if (!/(\\|\/)$/.test(tmpdir))
tmpdir += "\\";
var outfile = tmpdir + FSO.GetTempName();
var errfile = tmpdir + FSO.GetTempName();
// Note: See KB278411 for this recipe
// Note2: See cmd.exe /? for interesting quoting behavior...
var runcmd = '%comspec% /c "' + cmd + ' > "' + outfile + '" 2> "' + errfile + '""';
var wshexec = WShell.Exec(runcmd);
// Write stuff to the standard input of the command (through cmd.exe)
// Note: This will block until the program exits if significant amounts
// of information are written and not read. But no deadlock will occur.
// Note2: This will error if the program has exited
try {
wshexec.StdIn.Write("stuff\n");
} catch (ex) {
WScript.Echo("Unable to write to program.");
}
// Do stuff, or write more stuff while cmd executes, or wait...
while (wshexec.Status == 0)
WScript.Sleep(100);
exitcode = wshexec.ExitCode;
var output = "";
try {
var outfs = FSO.OpenTextFile(outfile, 1 /* ForReading */);
output = outfs.ReadAll();
outfs.Close();
FSO.DeleteFile(outfile);
} catch (ex) { }
var errors = "";
try {
var errfs = FSO.OpenTextFile(errfile, 1 /* ForReading */);
errors = errfs.ReadAll();
errfs.Close();
FSO.DeleteFile(errfile);
} catch (ex) { }
return { exitcode: exitcode, output: output, errors: errors };
}
result = run("dir");
WScript.Echo("Exit Code: " + result.exitcode);
WScript.Echo("Output:\n" + result.output);
WScript.Echo("Error Output:\n" + result.errors);
Remember, Don't ever WshShell.Exec a command directly if you are not sure of its inputs and outputs and your script deadlocking would be a problem.
Monday, February 8. 2010
Configuring Tomcat6 with Eclipse in Debian
After some moderate difficulty (mostly due to bugs 507536 and 552480) I have finally managed to setup Eclipse as a build environment for Apache Tomcat. The process is as follows (Note that $DEVELSERVERDIR
can be anything/anywhere that you have access to as a normal user):
- aptitude install eclipse tomcat6-user
- Install Java EE Development Tools plugin from the Eclipse WPT Project
- tomcat6-instance-create
$DEVELSERVERDIR
- cd
$DEVELSERVERDIR
- ln -s /usr/share/tomcat6/lib
- ln -s /usr/share/tomcat6/bin/bootstrap.jar bin
- cp /var/cache/tomcat6/catalina.policy conf
- Configure tomcat and eclipse to your liking and add the Tomcat instance to the Eclipse servers list
At this point you should be able to import/create projects which can be run on your local server instance. (If not, you are in the same boat I was in this morning... and best of luck to you)
Saturday, February 6. 2010
Removing All Nonpresent Devices with DevCon
Background
Windows remembers all devices which have previously been connected to the system and retains the drivers for those devices such that if the device is reconnected the driver searching and loading process can be avoided. This is a very useful behavior for devices which are commonly connected and disconnected (e.g. USB devices), but it has the side-effect that devices which are used once, or used for a finite amount of time, will forever be remembered by Windows. It is unclear exactly what "remember" means in this context, as I am not exactly sure what sort of storage or pre-loading of the drivers is occurring, but I can confirm that drivers for these "non-present" devices can affect the system, either with compatibility problems introduced by the driver or with performance problems from many accumulated "non-present" devices (Note: I doubt the performance impact is noticeable on all but extreme cases - like replacing your motherboard more than once with different hardware...).
Managing "Non-Present" Devices
The process for viewing these "non-present" devices in Device Manager is outlined in KB315539. They can also be viewed using the Microsoft DevCon utility. In fact, both programs can be used to remove, as well as view, the "non-present" devices. In Device Manager, the devices can be uninstalled just like any other. In DevCon, you must prefix the Device ID with a "@" in order for it to remove the device.
However, neither of these solutions allows for a quick method of removing all "non-present" devices, and on a system with hundreds of such devices this can be a significant hassle. To overcome this difficulty, I have written a script to remove all "non-present" devices which are not legacy or SW devices (since these are often required for other reasons - even when "non-present") using DevCon. The script can also read and write a file listing all devices to be removed (allowing for manual editing of the device list). You can download the script from the Remove Devices project page on the forge.
Thursday, February 4. 2010
OpenID Provider URL Formatting
We are making a website right now and we would like users to be able to login using their OpenID credentials. After an hour or so of googling I gave up trying to find a list of the OpenID URL formatting for the various OpenID providers (http://username.myopenid.com or http://flickr.com/username for example) and instead went through the MyOpenID site directory to compile my own list. Here it is in all of its glory:
Edit 2-4-2010: Today I went through and attempted to verify that all of these openid providers were good. The third column in the table represents my findings.
Google: | https://www.google.com/accounts/o8/id * | good |
Yahoo: | http://yahoo.com/ * | good |
MyOpenId: | http://username.myopenid.com | good |
LiveJournal: | http://username.livejournal.com | good |
AOL: | http://openid.aol.com/username | good |
WordPress: | http://username.wordpress.com | good |
Blogspot: | http://username.blogspot.com | must use blog url, blogspot = blogger |
Verisign: | http://username.pip.verisignlabs.com | good |
ClaimID: | http://openid.claimid.com/username | Signs in but nothing happens, appears to be broken on claimid end |
clickpass: | http://clickpass.com/public/username | I was only able to signup with IE and then there was a weird login procedure but it ultimately worked |
Google Profile: | http://google.com/profiles/username | good |
Blogger: | http://username.blogspot.com/ | good |
Flickr: | http://flickr.com/username | Couldn't get flickr to work |
identity.net: | http://username.identity.net/ | Never got sign up confirmation e-mail to test |
Bloglines: | http://username.bloglines.com/ | Didn't work |
Technorati: | http://technorati.com/people/technorati/username | Didn't work, reports of brokenness as of November of last year |
Vidoop: | http://username.myvidoop.com/ | good |
Vox: | http://username.vox.com/ | good |
MySpace: | http://myspace.com/username | Must make and use account url |
Musicpictures: | http://ww4.musicpictures.com/openid/username | Works fine but there is 0 openid advertising on the site |
Elgg: | http://explode.elgg.org/username | Tried registering two different places on elgg.org and neither worked. explode.elgg.org no longer exists so I think openid providing went with it. However, there is still an explode.elgg.org option in their openid login so I may be wrong. |
MyID: | http://username.myid.net/ | good - easiest sign up of any site I visited |
IdProxy: | http://username.idproxy.net/ | good |
Sxipper: | http://username.sxipper.com/ | Demo works with trainer id but it didn't work on the actual openid logins I tried. |
Signon: | http://username.signon.com/ | good |
|
|
Typekey is now part of typepad so I couldn't verify that typekey still works but typepad definitely does. |
Smugmug: | http://username.smugmug.com/ | Couldn't get to work with trial account, possibly works after paid? |
StartSSL: | https://username.startssl.com/ | This site is weird, I kept getting ssl errors when I went to sign up. I was told via e-mail however that startssl is an openid provider and their url is in the format listed here. |
Beemba: | http://username.beemba.com/ | beemba.com forwards to cliqset which does allow openid logins but as far as I can tell does not provide logins. |
Idtail: | http://username.idtail.com | Oriental site of some sort, couldn't get signed up but they do have openid login options |
*These sites do not require your username in the login string.
The login procedures at the different sites varied but my rough estimate says about 50% of the sites require you to know exactly what your personal URL is for the provider you are trying to login through (http://openid.aol.com/joojoo2988 to login with aol). Another 45% used some form of the RPX plugin and the final 5% rolled their own system that assisted with a small set of providers and allowed you to enter the URL for the rest (see Stack Overflow). This list is by no means exhaustive and if you know of another url you would like me to add to the list leave a comment or send an email.
Serious SPF/SenderID Problems in Exchange 2003
I was recently bitten by KB910272 (again). For the unfamiliar, this issue causes Sender ID/SPF records with non-class full subnets to be processed incorrectly and mail from those domains to be rejected. The number of such domains is surprisingly large for this problem to remain unsolved in the normal patch cycle... or so it seems to me right now.
To solve the problem, apply the hotfix from KB910272 or, even better, from one of the superseding updates: KB927478 or KB951639.
Tuesday, January 26. 2010
Full fledged online real estate calculator
And as a random aside: As with a lot of the open source projects R doesn't have the pretty UI but holy cow is it powerful. The DBI/RMySQL packages made interfacing with our database almost trivial. Many thanks to the people that contribute, I just can't say enough good things about R.
Wednesday, December 30. 2009
HTML Multiple Select Without Ctrl
The HTML select element has received much criticism when used for multiple selection over its usability problems. Although it may be simple enough for experienced users, it must be explained in detail (repeatedly) to the less experienced users, who will always find its behavior to be unintuitive. Recently we have come up against this problem and are attempting to work around it.
For some useful background and some great alternate implementations of multiple-selection see Ryan Cramer's analysis and replacement ideas as well as a jQuery Simple Multi-Select plugin which offers an on-demand replacement for the select multiple elements.
The approach that I took was to attempt to leave the select element alone as much as possible, since it is widely recognized and understood by HTML veterans (and already integrated in huge amounts of legacy code), and make it a little more user-friendly by removing the requirement of holding Ctrl to select multiple options. With the modifications in place, clicking on an option will toggle that option between selected and un-selected, while holding Ctrl or Shift will result in the same behavior as before. The solution is written in JavaScript with jQuery (although there is no reason it couldn't be reworked to remove the dependency on jQuery) and was completed without any browser sniffing (only feature-detection).
The script can be found in the forge on the Improve Multiple Select project page. The script can be directly downloaded from the files section (I suggest against directly linking to it - since the location may change). As always, feedback, suggestions for improvement, bug reports, etc. are greatly appreciated.
Monday, November 2. 2009
Bozeman house prices 11-1-2009
n=931
Price | Sq. Foot | Bedrooms | Bathrooms | Acres | |||
Min. | 19,900 | 437 | 1 | 1 | 0 | ||
Median | $329,900 | 2,214 | 3 | 3 | 0 | ||
Mean | $530,991 | 2,676 | 3.373 | 2.84 | 3.086 | ||
Max | $10,950,000 | 16,428 | 9 | 9 | 185 |
**Update 11-6-2009: The predictive algorithm has been much improved. I also added an option for Belgrade homes on the calculator which can be found here.
Wednesday, October 21. 2009
JavaScript image rotator
In an effort to make our page a little more personal and colorful I added an image rotator to our index page with pictures that I had taken around our office. There are about five million rotator scripts on the internet but I hadn't posted to the blog in a while so here is mine!
<html>
<body onLoad = "setImg();"><!-- Calls the setImg() function after the page has loaded -->
<span id="images"></span><!-- Image html will appear between these spans when the onLoad event is triggered -->
<script type="text/javascript">
//This is the array where you store the location of the pictures you would like to rotate through.
var imageArr = new Array("gfx/artofprogramming_small.jpg","gfx/thinkpad_small.jpg","gfx/theoffice_small.jpg","gfx/whiteboard_small.jpg","gfx/park_small.jpg","gfx/apple_small.jpg");
var numImages = imageArr.length;
function setImg(){
var randomNumber=Math.floor(Math.random()*numImages);//Selects a random number between 0 and the number of images in the array minus 1
document.getElementById("images").innerHTML = "<img src ='"+imageArr[randomNumber]+"' alt='rotating images from the office' />"; //Adds img html to your page between the two elements with id="images" }
</script>
</body>
</html>
Friday, October 16. 2009
HOWTO Modify Certificate Templates in SBS 2003
When using the Certificate Template MMC Snap-In to modify certificate templates in Windows Server 2003, any modifyable template is saved in the Version 2 certificate template format, which can only be used by Windows Server 2003 Enterprise Edition. This creates a significant annoyance when attempting to use certificate templates on a non-Enterprise Edition server (such as Small Business Server). However, when copying a Version 1 template (and making only minor modifications), there is no reason that the template can't be used as a Version 1 template, if you make some modifications by-hand.
Case in point: Setting up offline L2TP/IPSec Certificate Templates. If you have been following KB 555281, you are likely stuck at How to issue the custom L2TP/IPSec (Offline request) template. Here's the trick:
- Setup ADSI Edit, or any other LDAP editor
- Open adsiedit.msc (or your LDAP editor) and browse to CN=Configuration,CN=Services,CN=Public Key Services,CN=Certificate Templates
- Open the Properties for the template that you would like to use
- Change msPKI-Template-Schema-Version and msPKI-Template-Minor-Revision from 2 to 1 (not sure if msPKI-Template-Minor-Revision is really required...)
- Refresh the Certification Authority MMC Snap-In if it is open
That's it. If the template version is set to 1, you can issue the template in any edition of Server 2003.
Note: If you make substantial changes to the properties of the template this trick may not work. The differences in how the template versions are processed can be significant, but this process is likely to work for most simple changes to an existing Version 1 tepmlate.