diff options
-rw-r--r-- | README | 171 | ||||
-rw-r--r-- | README.md | 12 | ||||
-rw-r--r-- | answerfiles/SetupComplete.cmd (renamed from SetupComplete.cmd) | 0 | ||||
-rw-r--r-- | answerfiles/Setupca.vbs (renamed from Setupca.vbs) | 0 | ||||
-rw-r--r-- | answerfiles/adcertreq.inf.in (renamed from adcertreq.inf) | 2 | ||||
-rw-r--r-- | answerfiles/audituser.cmd (renamed from audituser.cmd) | 0 | ||||
-rw-r--r-- | answerfiles/dcinstall.ini.in (renamed from dcinstall.ini) | 10 | ||||
-rw-r--r-- | answerfiles/postinstall.cmd.in (renamed from postinstall.cmd) | 2 | ||||
-rw-r--r-- | answerfiles/setuppass2.cmd (renamed from setuppass2.cmd) | 0 | ||||
-rw-r--r-- | answerfiles/setuppass3.cmd (renamed from setuppass3.cmd) | 0 | ||||
-rw-r--r-- | answerfiles/specialize.cmd (renamed from specialize.cmd) | 0 | ||||
-rw-r--r-- | answerfiles/win2k8x8664.xml.in (renamed from win2k8x8664.xml) | 13 | ||||
-rw-r--r-- | make-ad-vm.sh | 74 |
13 files changed, 234 insertions, 50 deletions
@@ -0,0 +1,171 @@ +auto-win-vm-ad +============== + +Automatically create Windows Virtual Machines with Active Directory +and Certificate Services + +This allows you to create a Windows VM complete with Active Directory +and Certificate Services, and Active Directory TLS/SSL enabled, +completely automated and unattended. + +Currently only tested on (and hardcoded to work with) RHEL 6.3 with +kvm/qemu virtualization and Windows Server 2008 R2 Enterprise +Datacenter 64-bit. + +Pre-Requisites +============== + +These are the tools I've used so far: +* RHEL 6.3 64-bit with KVM/QEMU +** qemu-kvm - the basic virtualization packages +** python-virtinst - virt-install +** qemu-img +** libvirt-client - virsh +** dosfstools - mkfs.vfat +** openldap-clients - for testing the AD connection and getting the AD CA cert + +* en_windows_server_2008_r2_standard_enterprise_datacenter_web_x64_dvd_x15-50365.iso +** an MSDN subscription is required for access to Windows ISO files + and product keys +** I know 2008 R2 Enterprise Datacenter comes with Active Directory + and Certificate Services +** Not sure what other versions contain these +** autounattend.xml, dcinstall.ini, adcertreq.inf, and the cmd scripts + depend on this version + +* KVM/Machine setup +** In order to easily keep track of the VM hostname/IP address I have + done the following: +** edit /etc/hosts - assign an IP address and FQDN for the VM + e.g. something like this: + 192.168.122.2 ad.test.example.com ad +** The FQDN must be the first one listed (just like for SSL/Kerberos + testing) +** virsh net-edit default - add a name, the IP address from above, and + a unique MAC address +** This part can be automated too + +You will need to provide at least the name of the VM to the script. +The script will attempt to find the FQDN, the IP address, and the MAC +address (or you can provide these). + +Running +======= + +ANS_FILE_DIR=/path/to/thisscript/answerfiles \ +VM_NAME=nameofvm \ +ADMINPASSWORD="your windows admin password" \ +make-ad-vm.sh + +There are many, many parameters you can pass as environment variables +VM_NAME - name of virtual machine +- default - ad +VM_IMG_DIR - path to your KVM/QEMU disk images +* default - /var/lib/libvirt/images +ANS_FILE_DIR - path to the config files and scripts used during Windows install/setup +* no default - you must provide this +WIN_VER_REL_ARCH - windows version, release, arch +* default - win2k8x8664 +* must correspond to the WIN_ISO +WIN_ISO - the full path and file name of the Windows install ISO +* default - $VM_IMG_DIR/en_windows_server_2008_r2_standard_enterprise_datacenter_web_x64_dvd_x15-50365.iso +WIN_VM_DISKFILE - the full path and file name of the Windows VM disk image +* default - $VM_IMG_DIR/$VM_NAME.raw +VM_RAM - amount of RAM to use for VM, in MB +* default - 2048 (2GB) +VM_CPUS - number of CPUs to use for VM +* default - 2 +VM_DISKSIZE - size of WIN_VM_DISKFILE in GB +* default - 16 +ADMINNAME - user of Windows admin account +* default - Administrator +ADMINPASSWORD - password for ADMINNAME account +* no default - must be provided +VM_MAC - MAC address for VM +* default - will lookup from virsh net-dumpxml default for the VM name +VM_FQDN - fully qualified host and domain name for the VM +* default - will lookup from virsh net-dumpxml and getent hosts from the VM name +VM_CA_NAME - the name of the CA that will be created +* default - LMDN-LMHN-ca - where +** LMDN is the leftmost part of the domain name e.g. if your VM_FQDN is ad.test.example.com +** then the LMDN is "test" +** LMHN is the leftmost part of the FQDN e.g. if your VM_FQDN is ad.test.example.com +** then the LMHN is "ad" +** so the VM_CA_NAME would be test-ad-ca +VM_AD_DOMAIN - the domain of the AD server +* default - the part of VM_FQDN after the hostname part e.g. if VM_FQDN is ad.test.example.com +* the domain is test.example.com +VM_AD_SUFFIX - the AD root suffix +* default - derived from the VM_AD_DOMAIN - e.g. test.example.com -> dc=test,dc=example,dc=com +VM_NETBIOS_NAME - the NETBIOS domain name +* default - derived from VM_AD_DOMAIN e.g. test.example.com -> TESTEXAMPLECOM +ADMIN_DN - the full AD DN of the Windows Administrator +* default - cn=$ADMINNAME,cn=users,$VM_AD_SUFFIX + +Windows +======= +Windows supports unattended install and setup. In 2008 and some other +new-ish versions, this is done via a file called autounattend.xml. +When Windows boots off of the ISO, it looks for a file called +autounattend.xml in the root directory of all removable media. We use +a virtual floppy drive (answerfloppy.vfd) as drive A:\ in Windows and +put the file there. + +Setup goes through several different phases, or "passes" in Windows +parlance. The last pass is oobeSystem. It is during this pass that +we set the first of our "callback" scripts, postinstall.cmd. We first +set Windows to AutoLogin the Administrator so that we can use the +FirstLogonCommands and later RunOnce commands, and tell it to +AutoLogin 999 times. We use the FirstLogonCommands SynchronousCommand +to run the postinstall.cmd script. This script uses dcpromo.exe to +setup Active Directory with our chosen domain. It also activates +Windows with the specified product key. At the end, it creates the +RunOnce script for the next setup pass (setuppass2.cmd), and tells +Windows to reboot in 2 minutes. Active Directory requires a reboot in +order to complete the setup process. + +During the setuppass2 pass at next login, we install and configure +Certificate Services in Standalone Root CA mode (setupca.vbs /IS), +then set the RunOnce to run setuppass3.cmd, and reboot again in 2 +minutes. + +During the setuppass3 pass at next login, we generate an AD server +cert request, submit it to the CA, sign it, and install it in the AD +cert repo, using certreq and certutil. Once this is done, AD will +automatically configure itself to be a TLS/SSL server. + +The different Windows files are: +PLATFORM.xml - e.g. win2k8x8664.xml - this is copied to the virtual floppy disk as the main +autounattend.xml file +postinstall.cmd - activate Windows with the specified product key, setup AD, setup pass2, reboot +setuppass2.cmd - install and setup Cert Services, setup pass3, reboot +setuppass3.cmd - request and install AD server cert +dcinstall.ini - unattended setup file for AD +adcertreq.inf - unattended AD cert request file +Setupca.vbs - Virtual Basic script that installs and sets up Cert Services +otherfiles - currently unused + +Windows Troubleshooting +======================= +For general setup issues, look in c:\windows\panther\setup*.log + +dcpromo - c:\dcinstall.log +setupca.vbs - c:\_setupca.log +postinstall - c:\postinstall.log +setuppass2 - c:\setuppass2.log +setuppass3 - c:\setuppass3.log + +References +========== +Windows Unattended Installation Information for 2008 +* http://technet.microsoft.com/en-us/library/cc730695%28v=WS.10%29.aspx +Sample Unattend.xml files for 2008 +* http://technet.microsoft.com/en-us/library/cc732280(v=ws.10) +Enabling TLS/SSL in Active Directory +* http://www.richardhyland.com/diary/2009/05/12/installing-a-ssl-certificate-on-your-domain-controller/ +* http://support.microsoft.com/default.aspx?scid=kb;en-us;321051 +Windows certreq reference +* http://technet.microsoft.com/en-us/library/cc736326%28v=ws.10%29 +Source for Setupca.vbs +* http://blogs.technet.com/b/pki/archive/2009/09/18/automated-ca-installs-using-vb-script-on-windows-server-2008-and-2008r2.aspx +* http://technet.microsoft.com/en-us/library/ee918754(WS.10).aspx diff --git a/README.md b/README.md deleted file mode 100644 index bd13302..0000000 --- a/README.md +++ /dev/null @@ -1,12 +0,0 @@ -auto-win-vm-ad -============== - -Automatically create Windows Virtual Machines with Active Directory -and Certificate Services - -This allows you to create a Windows VM complete with Active Directory and -Certificate Services, and Active Directory TLS/SSL enabled, completely -automated and unattended. - -Currently only tested on (and hardcoded to work with) RHEL 6.3 with kvm/qemu -virtualization and Windows Server 2008 R2 Enterprise Datacenter 64-bit. diff --git a/SetupComplete.cmd b/answerfiles/SetupComplete.cmd index 70cfe12..70cfe12 100644 --- a/SetupComplete.cmd +++ b/answerfiles/SetupComplete.cmd diff --git a/Setupca.vbs b/answerfiles/Setupca.vbs index 6a5566d..6a5566d 100644 --- a/Setupca.vbs +++ b/answerfiles/Setupca.vbs diff --git a/adcertreq.inf b/answerfiles/adcertreq.inf.in index 5147677..028c482 100644 --- a/adcertreq.inf +++ b/answerfiles/adcertreq.inf.in @@ -4,7 +4,7 @@ Signature="$Windows NT$" [NewRequest] -Subject = "CN=ad.test.example.com" +Subject = "CN=@VM_FQDN@" KeySpec = 1 KeyLength = 2048 ; Can be 1024, 2048, 4096, 8192, or 16384. diff --git a/audituser.cmd b/answerfiles/audituser.cmd index 116dc7d..116dc7d 100644 --- a/audituser.cmd +++ b/answerfiles/audituser.cmd diff --git a/dcinstall.ini b/answerfiles/dcinstall.ini.in index 43c029d..2810077 100644 --- a/dcinstall.ini +++ b/answerfiles/dcinstall.ini.in @@ -1,11 +1,11 @@ [DCINSTALL] -UserName=Administrator -Password=Secret123 +UserName=@ADMINNAME@ +Password=@ADMINPASSWORD@ ReplicaOrNewDomain=Domain NewDomain=Forest -NewDomainDNSName=test.example.com +NewDomainDNSName=@VM_AD_DOMAIN@ ForestLevel=4 -DomainNetbiosName=TESTEXAMPLECOM +DomainNetbiosName=@VM_NETBIOS_NAME@ DomainLevel=4 InstallDNS=Yes ConfirmGC=Yes @@ -13,5 +13,5 @@ CreateDNSDelegation=No DatabasePath="C:\Windows\NTDS" LogPath="C:\Windows\NTDS" SYSVOLPath="C:\Windows\SYSVOL" -SafeModeAdminPassword=Secret123 +SafeModeAdminPassword=@ADMINPASSWORD@ RebootOnCompletion=No diff --git a/postinstall.cmd b/answerfiles/postinstall.cmd.in index 71f960a..5dd05fd 100644 --- a/postinstall.cmd +++ b/answerfiles/postinstall.cmd.in @@ -1,6 +1,6 @@ echo these are commands to be run upon first login post installation rem echo activate windows with the product key -rem cscript c:\Windows\System32\slmgr.vbs /ipk "the product key" +rem cscript c:\Windows\System32\slmgr.vbs /ipk "@PRODUCT_KEY@" rem cscript c:\Windows\System32\slmgr.vbs /ato echo Setup AD as Domain Controller %SystemRoot%\System32\dcpromo.exe /unattend:a:\dcinstall.ini > c:\dcinstall.log 2>&1 diff --git a/setuppass2.cmd b/answerfiles/setuppass2.cmd index 42426c6..42426c6 100644 --- a/setuppass2.cmd +++ b/answerfiles/setuppass2.cmd diff --git a/setuppass3.cmd b/answerfiles/setuppass3.cmd index d93377e..d93377e 100644 --- a/setuppass3.cmd +++ b/answerfiles/setuppass3.cmd diff --git a/specialize.cmd b/answerfiles/specialize.cmd index c6f252a..c6f252a 100644 --- a/specialize.cmd +++ b/answerfiles/specialize.cmd diff --git a/win2k8x8664.xml b/answerfiles/win2k8x8664.xml.in index 669775a..54918db 100644 --- a/win2k8x8664.xml +++ b/answerfiles/win2k8x8664.xml.in @@ -46,9 +46,8 @@ <AcceptEula>true</AcceptEula> <!-- <ProductKey> - <Key>GCRM3-DH8B8-49PTX-QRB6X-K28GD</Key> + <Key>@PRODUCT_KEY@</Key> </ProductKey> -need real product key --> </UserData> <!-- @@ -91,7 +90,7 @@ need real product key </settings> <settings pass="specialize"> <component name="Microsoft-Windows-Shell-Setup" processorArchitecture="amd64" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS" xmlns:wcm="http://schemas.microsoft.com/WMIConfig/2002/State" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> - <ComputerName>AD</ComputerName> + <ComputerName>@VM_NAME@</ComputerName> </component> <component name="Microsoft-Windows-Deployment" processorArchitecture="x86" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS" xmlns:wcm="http://schemas.microsoft.com/WMIConfig/2002/State" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> <RunSynchronous> @@ -124,13 +123,13 @@ need real product key <component name="Microsoft-Windows-Shell-Setup" processorArchitecture="amd64" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS" xmlns:wcm="http://schemas.microsoft.com/WMIConfig/2002/State" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> <UserAccounts> <AdministratorPassword> - <Value>Secret123</Value> + <Value>@ADMINPASSWORD@</Value> <PlainText>true</PlainText> </AdministratorPassword> <LocalAccounts> <LocalAccount wcm:action="add"> <Password> - <Value>Secret123</Value> + <Value>@ADMINPASSWORD@</Value> <PlainText>true</PlainText> </Password> <Description>testadmin</Description> @@ -142,12 +141,12 @@ need real product key </UserAccounts> <AutoLogon> <Password> - <Value>Secret123</Value> + <Value>@ADMINPASSWORD@</Value> <PlainText>true</PlainText> </Password> <Enabled>true</Enabled> <LogonCount>999</LogonCount> - <Username>Administrator</Username> + <Username>@ADMINNAME@</Username> </AutoLogon> <FirstLogonCommands> <SynchronousCommand wcm:action="add"> diff --git a/make-ad-vm.sh b/make-ad-vm.sh index 298ab06..5191578 100644 --- a/make-ad-vm.sh +++ b/make-ad-vm.sh @@ -1,22 +1,37 @@ #!/bin/sh # lots of parameters to set or override -VM_IMG_DIR=${VM_IMG_DIR:-/export1/kvmimages} +VM_IMG_DIR=${VM_IMG_DIR:-/var/lib/libvirt/images} ANS_FLOPPY=${ANS_FLOPPY:-$VM_IMG_DIR/answerfloppy.vfd} -ANS_FILE_DIR=${ANS_FILE_DIR:-/share/auto-win-vm-ad} +ANS_FILE_DIR=${ANS_FILE_DIR:-/share/auto-win-vm-ad/answerfiles} FLOPPY_MNT=${FLOPPY_MNT:-/mnt/floppy} WIN_VER_REL_ARCH=${WIN_VER_REL_ARCH:-win2k8x8664} WIN_ISO=${WIN_ISO:-$VM_IMG_DIR/en_windows_server_2008_r2_standard_enterprise_datacenter_web_x64_dvd_x15-50365.iso} -WIN_VM_DISKFILE=${WIN_VM_DISKFILE:-$VM_IMG_DIR/ad.raw} # windows server needs lots of ram, cpu, disk VM_RAM=${VM_RAM:-2048} VM_CPUS=${VM_CPUS:-2} VM_DISKSIZE=${VM_DISKSIZE:-16} VM_NAME=${VM_NAME:-ad} - -if [ -z "$AD_ROOTPW" ] ; then - echo Error: you must supply the password for $AD_ROOTDN - echo in the AD_ROOTPW environment variable +WIN_VM_DISKFILE=${WIN_VM_DISKFILE:-$VM_IMG_DIR/$VM_NAME.raw} +ADMINNAME=${ADMINNAME:-Administrator} + +# fix .in files +do_subst() +{ + sed -e "s/@ADMINPASSWORD@/$ADMINPASSWORD/g" \ + -e "s/@DOMAINNAME@/$VM_AD_DOMAIN/g" \ + -e "s/@ADMINNAME@/$ADMINNAME/g" \ + -e "s/@VM_AD_DOMAIN@/$VM_AD_DOMAIN/g" \ + -e "s/@VM_NETBIOS_NAME@/$VM_NETBIOS_NAME/g" \ + -e "s/@VM_NAME@/$VM_NAME/g" \ + -e "s/@VM_FQDN@/$VM_FQDN/g" \ + -e "s/@PRODUCT_KEY@/$PRODUCT_KEY/g" \ + $1 +} + +if [ -z "$ADMINPASSWORD" ] ; then + echo Error: you must supply the password for $ADMINNAME + echo in the ADMINPASSWORD environment variable exit 1 fi @@ -47,32 +62,43 @@ fi # now that we have the fqdn, construct our suffix lmhn=`echo $VM_FQDN | sed -e 's/^\([^.]*\).*$/\1/'` domain=`echo $VM_FQDN | sed -e 's/^[^.]*\.//'` -lmdn=`echo $domain | sed -e 's/^\([^.]*\).*$/\1/'` -suffix=`echo $domain | sed -e 's/^/dc=/' -e 's/\./,dc=/g'` +VM_AD_DOMAIN=${VM_AD_DOMAIN:-"$domain"} +lmdn=`echo $VM_AD_DOMAIN | sed -e 's/^\([^.]*\).*$/\1/'` +suffix=`echo $VM_AD_DOMAIN | sed -e 's/^/dc=/' -e 's/\./,dc=/g'` +netbios=`echo $VM_AD_DOMAIN | sed -e 's/\.//g' | tr '[a-z]' '[A-Z]'` VM_CA_NAME=${VM_CA_NAME:-"$lmdn-$lmhn-ca"} VM_AD_SUFFIX=${VM_AD_SUFFIX:-"$suffix"} -AD_ROOTDN=${AD_ROOTDN:-"cn=administrator,cn=users,$VM_AD_SUFFIX"} - +VM_NETBIOS_NAME=${VM_NETBIOS_NAME:-"$netbios"} +ADMIN_DN=${ADMIN_DN:-"cn=$ADMINNAME,cn=users,$VM_AD_SUFFIX"} if [ ! -f $ANS_FLOPPY ] ; then - mkfs.vfat -C $ANS_FLOPPY 1440 || { echo error $! from mkfs.vfat -C $ANS_FLOPPY 1440 ; exit 1 ; } + mkfs.vfat -C $ANS_FLOPPY 1440 || { echo error $? from mkfs.vfat -C $ANS_FLOPPY 1440 ; exit 1 ; } fi if [ ! -d $FLOPPY_MNT ] ; then - mkdir -p $FLOPPY_MNT || { echo error $! from mkdir -p $FLOPPY_MNT ; exit 1 ; } - + mkdir -p $FLOPPY_MNT || { echo error $? from mkdir -p $FLOPPY_MNT ; exit 1 ; } fi -mount -o loop -t vfat $ANS_FLOPPY $FLOPPY_MNT || { echo error $! from mount -o loop -t vfat $ANS_FLOPPY $FLOPPY_MNT ; exit 1 ; } - -cp $ANS_FILE_DIR/$WIN_VER_REL_ARCH.xml $FLOPPY_MNT/autounattend.xml || { echo error $! from cp $ANS_FILE_DIR/$WIN_VER_REL_ARCH.xml $FLOPPY_MNT/autounattend.xml ; umount $FLOPPY_MNT ; exit 1 ; } - -# convert to DOS format to make it easier to read on windows -for file in adcertreq.inf setuppass3.cmd setuppass2.cmd dcinstall.ini postinstall.cmd specialize.cmd Setupca.vbs SetupComplete.cmd audituser.cmd ; do - sed 's/$/
/' $ANS_FILE_DIR/$file > $FLOPPY_MNT/$file || { echo error $! from sed $ANS_FILE_DIR/$file to $FLOPPY_MNT/$file ; umount $FLOPPY_MNT ; exit 1 ; } +mount -o loop -t vfat $ANS_FLOPPY $FLOPPY_MNT || { echo error $? from mount -o loop -t vfat $ANS_FLOPPY $FLOPPY_MNT ; exit 1 ; } + +# replace .in files with the real data +# convert to DOS format to make them easier to read in Windows +for file in $ANS_FILE_DIR/* ; do + err= + case $file in + *$WIN_VER_REL_ARCH.xml*) outfile=$FLOPPY_MNT/autounattend.xml ;; + *) outfile=$FLOPPY_MNT/`basename $file .in` ;; + esac + case $file in + *.in) do_subst $file | sed 's/$/
/' > $outfile || err=$? ;; + *) sed 's/$/
/' $file > $outfile || err=$? ;; + esac + if [ -n "$err" ] ; then + echo error $err copying $file to $outfile ; umount $FLOPPY_MNT ; exit 1 + fi done -umount $FLOPPY_MNT || { echo error $! from umount $FLOPPY_MNT ; exit 1 ; } +umount $FLOPPY_MNT || { echo error $? from umount $FLOPPY_MNT ; exit 1 ; } serialpath=/tmp/serial-`date +'%Y%m%d%H%M%S'`.$$ @@ -83,7 +109,7 @@ virt-install --connect=qemu:///system --hvm \ --disk path=$WIN_VM_DISKFILE,bus=ide,size=$VM_DISKSIZE,format=raw,cache=none \ --disk path=$ANS_FLOPPY,device=floppy \ --network=bridge=virbr0,model=rtl8139,mac=$VM_MAC \ - $VI_DEBUG --noautoconsole || { echo error $! from virt-install ; exit 1 ; } + $VI_DEBUG --noautoconsole || { echo error $? from virt-install ; exit 1 ; } echo now we wait for everything to be set up TRIES=100 @@ -110,7 +136,7 @@ CA_CERT_DN="cn=$VM_CA_NAME,cn=certification authorities,cn=public key services,c TMP_CACERT=/tmp/cacert.`date +'%Y%m%d%H%M%S'`.$$.pem echo "-----BEGIN CERTIFICATE-----" > $TMP_CACERT -ldapsearch -xLLL -H ldap://$VM_FQDN -D "$AD_ROOTDN" -w "$AD_ROOTPW" -s base -b "$CA_CERT_DN" "objectclass=*" cACertificate | perl -p0e 's/\n //g' | sed -e '/^cACertificate/ { s/^cACertificate:: //; s/\(.\{1,64\}\)/\1\n/g; p }' -e 'd' | grep -v '^$' >> $TMP_CACERT +ldapsearch -xLLL -H ldap://$VM_FQDN -D "$ADMIN_DN" -w "$ADMINPASSWORD" -s base -b "$CA_CERT_DN" "objectclass=*" cACertificate | perl -p0e 's/\n //g' | sed -e '/^cACertificate/ { s/^cACertificate:: //; s/\(.\{1,64\}\)/\1\n/g; p }' -e 'd' | grep -v '^$' >> $TMP_CACERT echo "-----END CERTIFICATE-----" >> $TMP_CACERT echo Now test our CA cert |