This is a module that is near and dear to my heart! I perform 4-5 installs per day, and I've had a working installer for for about 6-7 months now. While I'd be embarrassed to post any of the code (scope creep has made it a tangled mess) I can share some lessons learned.
Separate the GUI and the logic: Separating the GUI and the logic has allowed me to have both a windows forms and a web application version. When I'm at my desk, I always use the windows forms version for speed purposes. However, when I'm in the field I can always access the web version from any PC.
Providers: I learned this one the hard way. I currently have five providers: Authentication, File System, SQL Server, Web Service, Permissions.
The authentication provider allows me to authenticate a user against my hosting control panel. This could be optional because not everyone will need it, but allowing for it now will attract more hosting companies to DotNetNuke.
The file system provider deletes exiting files; modifies files with connection info, user passwords, etc.; and installs the files. The biggie here is that some installer users will want to the local file system, some may want webDav, and some will want FTP.
The SQL Server provider creates the login and database. In my installer I allow for upgrades and fresh installs. During a fresh install if a selected db has tables or sps, the db is dropped and recreated (after user confirmation of course). My current provider works with my control panel to list logins and dbs, and to create the database. However hosts can write their own providers and the installer should include a base provider to create the db locally or remotely.
The web service provider just ensures that ASP.NET is enabled and that if a sub-web is selected it's an application folder. Again, mine is custom for my control panel, but the installer should allow for local and remote usage.
The permissions provider checks the required permissions and sets them if they are not correct.
Optional Files: This is a cool one I just added last week. By listing optional files, the installer can place chosen modules, skins, containers, and languages in the \install folder. This allows the user to select optional components during the install.
Just my two cents, hope it helps!