• Dual Boot Vista with Windows Server 2008

    Some time ago I built my laptop with dual boot and had a few issues along the way. I thought it might be worth sharing, but have only just got round to sharing this information. This information works equally as well with Windows 7 and Windows Server 2008 R2.

    ISSUE 1 (Bitlocker PIN enabling for Dual boot partitions that may not be on a domain)

    I wanted to dual boot with Vista x64 and Windows 2008 (actually triple boot, but this information is useful all ways). And I wanted all partitions (with the exception of the boot partition) to be protected with Bitlocker. I couldn’t seem to get Bitlocker to use a PIN for all boot partitions (the other 2 partitions were not going to live on any domain; well not immediately anyway).

    Unfortunately, I had blindly enabled Bitlocker before ensuring the Advanced Settings were enabled (this can be set to be enabled by default on a domain), which allows the setting of a PIN or USB key. Further it was looking increasingly like I had to decrypt the partition, enable the advanced settings and then re-encrypt (while at the same time setting a PIN). I did a little bit of searching around and came up with the following (in essence you can enable Advanced Bitlocker settings post encryption (the easy bit) and then create a PIN afterwards (even easier, but not well known)).

    Enable Advanced Bitlocker Settings As per points 1 -6 in Section “To turn on BitLocker Drive Encryption with a TPM plus a PIN or with a TPM plus a startup key on a USB flash drive” in this article http://technet.microsoft.com/en-us/library/cc766295.aspx (extract below)

    1. Click Start, type gpedit.msc in the Start Search box, and then press ENTER.

    2. If the User Account Control dialog box appears, verify that the proposed action is what you requested, and then click Continue. For more information, see Additional Resources later in this document.

    3. In the Group Policy Object Editor console tree, click Local Computer Policy, click Administrative Templates, click Windows Components, and then double-click BitLocker Drive Encryption.

    4. Double-click the setting Control Panel Setup: Enable Advanced Startup Options. The Control Panel Setup: Enable Advanced Startup Options dialog box appears.

    5. Select the Enabled option. For TPM plus a PIN or startup key configurations, you do not need to change any further settings, but you can choose to require or disallow users to create a startup key or PIN. Click OK.

    6. Click Start, type gpupdate.exe /force in the Search box, and then press ENTER. Wait for the process to finish.

    This doesn’t give you the Option\Dialog to set\create a PIN when the volume is already encrypted, which is a bit of a pain.

    Create a PIN for the partition

    1. Open a Command prompt as Administrator

    2. Use the following command “cscript %systemroot%\system32\manage-bde.wsf –protectors –add %systemdrive% -tpmandpin <4-20 digit numeric PIN>” (without the quotes)

    3. Now to be really sure the PIN is was you want it to be and you don’t trust what you typed in replacement for  <4-20 digit numeric PIN> above, Open Control Panel | Security | Bitlocker Drive Protection and choose Manage Bitlocker Keys for the current Volume, and there should now be a Reset PIN option.

    4. Job done. Told you is was even easier (easier being less clicks of the mouse).

    ISSUE 2 (well not really an issue, but something that might help in sorting out the list of OS’s of the bootloader)

    So, I installed Vista x64 then Windows 2008 and another Vista (x86 this time); don’t ask me why I just did, and it suits my needs. The bootloader initial screen showed the really Useful choices of

    Microsoft Windows Vista

    Microsoft Windows Server 2008

    Microsoft Windows Vista

    Which was which Vista, well in time I got used to it and the domain connected one was the default, but due to my installation order it was the third in the list; not very intuitive methinks.

    What I really wanted was

    Microsoft Windows Vista x64

    Microsoft Windows Server 2008

    Microsoft Windows Vista x86

    Firstly the following can be used to reorder the display list

    Open a Command prompt as Administrator

    Before you do anything backup your BCD by using bcdedit /export “c:\bcdbackup1” (where C: is the volume your working on). If you make any mistakes in the next bits you can restore it by using bcdedit /import “c:\bcdbackup1”

    This simply changes the order in which bootloader displays the OS’s bcdedit /displayorder {ID1} ID2} {ID3} where {ID1}, {ID2} and {ID3} can be determined by typing just bcdedit at the command prompt and noting the entries adjacent to displayorder. Just put them in the order that you want them when creating the command line above e.g. my display order under Windows BootMgr looked like this  (where {current} is the primary x64 install of Vista. It will show as {default} if bcdedit is run in any of the other OS’s and {current } will obviously be the current OS you are working in. Be aware of this and use the exact GUID or words that are listed opposite displayorder, otherwise there will be tears)

    DualBootImage001

    But I wanted it to look like this

    clip_image004

    And this is the command line I used bcdedit /displayorder {current} {3bfc9072-594d-11dd-8d96-c955ae3305ea} {cf632714-6411-11dd-95e7-d088af2f2b01}

    Secondly lets rename the entries in the list (it would be useful if the x64 installation displayed as “Microsoft Windows Vista x64” and the x86 version likewise)

    Open a Command prompt as Administrator

    Before you do anything backup your BCD by using bcdedit /export “c:\bcdbackup2” (where C: is the volume your working on). If you make any mistakes in the next bits you can restore it by using bcdedit /import “c:\bcdbackup2”

    To change the display name in the bootloader list type the following at the command prompt bcdedit –set {current} DESCRIPTION “Microsoft Windows Vista x64” (the quotes are needed and {current } is the entry I wanted to change. If the Identifier is a GUID or {default} use that). And this is what you see when you run bcdedit again.

    clip_image006

  • Get User Profiles and Last User that Logged on to Desktop Computers

    Another Powershell script that helps with a migration. In this instance the requirement was for a way of understanding the accounts that had previously logged onto desktop systems and the credentials of the last logged on user, for either all desktop systems in a Domain\OU structure or all desktop systems listed in a text file. Additionally, in this instance, there was a requirement to add a delay between each iteration of the data gathering to minimise impact on the script running system and to the network.

    The challenge was that this information is stored differently in XP and Vista\Win7 in the registry and the Profile information is accessible on Vista\Win7 systems using Get-Profiles, but needs some registry scripting jiggery-pokery for XP\Win2k3 systems. Additionally, I could not find any script out there that did both these things, so I decided to use functions from other peoples work and stitch them together to get what is attached.

    To understand what can be done with the script, read the header. If the script is run without parameters it will just get Profile information for all desktop systems in the currently logged on domain. The output is stored in 3 csv files that are suffixed with Date\Time stamps in the same location that the script is run from.

     The Syntax for running the script is this and none of the parameters are required.

    .\Get-PCUserProfilePath.ps1 -srcDomainPath <SearchPathdnOrDNSName> -IterationDelay <TimeInSeconds> -ComputerType <ServerOrDesktop> -ComputerListFile <sAMAccountNameTextList>
     

    I have not included a listing of the script here because it is too large, but the script can be downloaded at the bottom of this posting.

    HTH

     

  • Delete Stuff in AD

    Once again another script to help one of my colleagues in need of a method of bulk deleting objects in AD taken from a list in CSV file. In this instance he need it for deletion of groups that they had determined as no longer useful. Bizarrely, this type of script did not exist when he searched for it (I would have thought someone would have written something like this previously). Actually I had some written some of this code already over 8 years ago and decided to repurpose it for my colleague.

    Below is a listing of the VBScript. It reads in a file named Groups.csv that contains a list of all groups (sAMAccountName's) to be deleted (the original CSV file also had a second column that had the group type integer, but the script strips this). The script works in the domain of the currently logged on credentials, so you need the necessary permissions in AD for it to work.

    Normally, I comment my scripts a lot more, but this was a rush order :-) and I haven't had the time to revisit it (and I an trying to move from VBScript now).

     

    My colleague has proven, the script is easily altered to enable it t delete any type of object and these scripts have been posted to Microsoft Script Center.

     

    'Script deletes security groups from a csv file.
    'csv format is strsAMGroupName,Whatever
    'This script is offered with no warranty
    'On Error Resume Next 'used in case group not found
    Option Explicit

    Const ForReading = 1

    Dim strL, spl1, strOU, strGroupCN, strGroupName
    Dim objFSO, objInputFile

    Set objFSO = CreateObject("Scripting.FileSystemObject")

    Set objInputFile = objFSO.OpenTextFile(".\groups.csv", ForReading) 'your csv file

    wscript.echo "script started"

    'extract from csv file
    Do until objInputFile.AtEndOfStream
     strL = objInputFile.ReadLine
     spl1 = Split(strL, ",")
     strGroupName = (spl1(0))
     If GroupExists(strGroupName) = True Then
      'WScript.Echo strGroupName & " exists."
      DelGroup
     End If   
    Loop

    Set objFSO = Nothing
    Set objInputFile = Nothing

    wscript.echo "script finished"

    'group exist check
    Function GroupExists(strsAMGroupName)

    Dim strDNSDomain, strFilter, strQuery
    Dim objConnection, objCommand, objRootLDAP, objLDAPGroup, objRecordSet

    GroupExists = False
    Set objConnection = CreateObject("ADODB.Connection")
    Set objCommand =   CreateObject("ADODB.Command")
    Set objRootLDAP = GetObject("LDAP://RootDSE")
    objConnection.Provider = "ADsDSOObject"
    objConnection.Open "Active Directory Provider"
    Set objCommand.ActiveConnection = objConnection
    objCommand.Properties("Page Size") = 1000
    'objCommand.Properties("Searchscope") = ADS_SCOPE_SUBTREE

    strDNSDomain = objRootLDAP.Get("DefaultNamingContext")
    strFilter = "(&(objectCategory=group)(sAMAccountName=" & strsAMGroupName & "))"

    strQuery = "<LDAP://" & strDNSDomain & ">;" & strFilter & ";sAMAccountName,adspath,CN;subTree"

    objCommand.CommandText = strQuery
    'WScript.Echo strFilter
    'WScript.Echo strQuery
    Set objRecordSet = objCommand.Execute

    If objRecordSet.RecordCount = 1 Then

    objRecordSet.MoveFirst
        'WScript.Echo "We got here " & strsAMGroupName     
     'WScript.Echo objRecordSet.Fields("sAMAccountname").Value
     'WScript.Echo objRecordSet.Fields("adspath").Value
     If objRecordSet.Fields("sAMAccountname").Value = strsAMGroupName Then
      GroupExists = True
      Set objLDAPGroup = GetObject(objRecordSet.Fields("adspath").Value)
      strOU = objLDAPGroup.Parent
      strGroupCN = objRecordSet.Fields("CN").Value
     End If
    Else
     WScript.Echo strsAMGroupName & " Group doesn't exist or Duplicate sAMAccountName"
     GroupExists = False
     strGroupCN = ""
     strOU = ""
    End If

    objRecordSet.Close
    Set objConnection = Nothing
    Set objCommand = Nothing
    Set objRootLDAP = Nothing
    Set objLDAPGroup = Nothing
    Set objRecordSet = Nothing

    end function

    Sub DelGroup

    Dim objOU

    'WScript.Echo strOU
    'WScript.Echo strGroupCN
    Set objOU = GetObject(strOU)
    objOU.Delete "Group", "cn=" & strGroupCN & ""
    WScript.Echo strGroupName & " (CN=" & strGroupCN & ") has been deleted."

    Set ObjOU = Nothing
    strGroupCN = ""

    End Sub

     

  • Dual Boot from VHD

    When Windows 7 Beta came out, a colleague and I decided that the being able to dual boot from a VHD file would be useful for demos and to test Windows7 and Windows Server 2008 R2, due to the ability to replace and service VHD files pretty much at will (VHD files can be mounted as volumes in Vista http://blogs.msdn.com/cschotte/archive/2008/03/26/how-to-mount-a-vhd-quickly-under-vista-using-your-mouse.aspx and it is even easier in Windows 7 using the task Attach VHD in Disk Management). So I pulled together some information from various sources and wrote a script (BootFromVHD.vbs) to come up with a process that quickly enables booting from a VHD file.

    This method does not provide any way of single booting from a VHD and assumes that either Windows 7\Windows Server 2008 R2 are already installed and running.

    Create a VHD

    To start with a VHD is required and this will only work with a Windows 7 or Windows Server 2008 R2 VHD. I personally found it easier to create a vanilla Windows Server 2008 R2 (in my case; but it works equally well with Windows 7) virtual guest in Windows Server 2008 Hyper-V with a fixed disk size. Then while the guest virtual computer is still running on the Hyper-V server I ran the following command from an elevated (administrator) command prompt within the guest “c:\windows\system32\sysprep\sysprep /generalize /oobe /shutdown”. This basically, re-bases the image and removes anything specific to the hardware it was built on (in this case a virtual environment).

    Alternatively, you could use this method to create a VHD from a WIM image.

    1. Download Win7 WAIK - http://www.microsoft.com/downloads/details.aspx?FamilyID=696dd665-9f76-4177-a811-39c26d3b3b34&displaylang=en

    2. Download WIM2VHD - http://code.msdn.microsoft.com/wim2vhd

    3. Windows 7 or Windows 2008 R2 installation media required.

    4. Install WAIK on a Windows 7 client

    5. Run WIM2VHD using this command line (this can be modified for Win7 or other version of 2008 R2; see the examples on the WIM2VHD page):

    cscript wim2vhd.wsf /wim:path /sku:serverstandard /vhd:path /size:15360 /disktype:fixed

    Once you have a VHD file it needs to be copied to the hardware from which it will boot.

    Very Important Notes:

    • The VHD can reside in a folder on the volume on which you copy it, but the VHD needs to reside on a volume that does not have Bitlocker enabled.
    • Also, the VHD needs to be less than the size of the partition it is stored on minus the Pagefile size (VHD size + PageFile Size <= Storage partition Size).
    • Booting from VHD does not work with VHD’s created in Virtual PC.
    • If the VHD is dynamically expanding, when it first opens it will convert to a full fixed size VHD and this may take a very long time. This will fail if the volume the VHD sits on is not sized correctly see note above.
    • This is a vanilla install and will result in the “out of the box” installation running to configure for your hardware. You may need to provide drivers to get it all working.
    • Each tool provides its own instructions on how it works and what changes will be made.

    Add an entry in the BCD to boot from the VHD

    This can be done manually by using an elevated command prompt in the original operating system on the computer using the Windows 7 version of BCDEdit.exe, or by script (VBScript or PowerShell). I did tried to write a VBScript that uses just WMI to carry out this task, but unfortunately I couldn’t get the BCDEdit WMI provider to recognise that I had created a new entry for the rest of the script to edit. So, I went with the method of calling BCDEdit.exe from within the VBScript.

    I won’t explain the method of doing this manually, but will provide the script below. Please be aware that I or my employer holds no responsibility for your using this script and the outcomes thereof and I provide the code without guarantees or warranties (basically, test it prior to using it and ensure you are happy with what it is doing; myself and many of my colleagues have used this and have only seen one failure; which was not terminal). The script does create a backup of your existing BCD and will roll back any changes if any errors are encountered. If for some reason you want to roll back the BCD changes all you need to do is run “bcdedit /import c:\bcdbackup” from an elevated command prompt.

    Notes

    • The script needs to be run using cscript in an elevated command prompt.
    • Carefully read the instructions of each popup dialog box to ensure you input the correct information. Firstly you must provide a name for the BCD entry and secondly the location of the VHD does not require the drive specifier (the bootloader will find what volume the file is stored on at boot time).

    '*************************************************************************************************************

    '* bcdbootfromvhd.vbs

    '*

    '* Purpose: Creates a new BCD Boot Loader Entry by copying an

    '* existing entry and making changes

    '*

    '* Parms: None.

    '*

    '* Requires: To be run on Vista\Windows 7 or Windows Server 2008 (R2) to access BcdStore using

    '* BCDEDIT and WMI.

    '*

    '* References: 1) Vista Software Development Kit.

    '* 2) MSDN Library, "Boot Configuration Data (BCD)" at

    '* http://msdn2.microsoft.com/en-us/library/aa362692.aspx

    '* 3) "Boot Configuration Data in Windows Vista" at

    '* http://download.microsoft.com/download/a/f/7/af7777e5-7dcd-4800-8a0a-b18336565f5b/BCD.doc

    '*

    '* Note : This function should be invoked as a cscript.

    '* Issue the command "cscript //h:cscript" to set the

    '* default scripting as cscript before issuing this

    '* command.

    '*

    '* Author : Carl Harrison

    '*************************************************************************************************************

    Option Explicit

    On Error Resume Next

    Dim strComputer

    Dim strNewLoaderGUID

    Dim strVHDPath, strBootEntryName

    Dim objStoreClass, objStore, objDefault, objElement

    Dim objShell, objBCDEditCopyCmd

    Dim strCommandOutputReadLine

    Dim varDefaultLoader

    Const BcdLibraryString_Description = &h12000004

    Const Current = "{fa926493-6f1c-4193-a414-58f0b2456d1e}"

    Const WindowsImages = &h10200003

    strComputer = "."

    'Connect to the BCD store with WMI

    Set objStoreClass = GetObject("winmgmts:{(Backup,Restore)}\\" & strComputer & "\root\wmi:BcdStore")

    if not objStoreClass.OpenStore("", objStore) then

         WScript.Echo "Couldn't open the system store!"

         WScript.Quit

    end if

     

    'Get some info about the current booted OS from the BCD

    'We are going to use it later

    objStoreClass.OpenStore "", objStore

    objStore.OpenObject Current, objDefault

    objDefault.GetElement BcdLibraryString_Description, objElement

    Set objShell = CreateObject("Wscript.Shell")

     

    'Get a backup of the existing BCD

    Set objBCDEditCopyCmd = objShell.Exec("bcdedit /export c:\bcdbackup")

    If Instr(objBCDEditCopyCmd.StdOut.ReadAll,"The operation completed successfully.") <> 0 then

         WScript.Echo "BCD Backup was created successfully"

    Else

         WScript.Echo "Failed to create BCD Backup"

         WScript.Echo "Script is Exiting"

         WScript.Quit

    End If

     

    'Popup dialog to ask for the BCD entry human friendly name

    'We don’t accept blanks

    strBootEntryName = InputBox ("Name of Boot loader entry at Start Up e.g. Windows 7")

    If strBootEntryName = "" then

         WScript.Echo "Blank Loader Entry Name is not accepted."

         WScript.Echo "ROLLING BACK CHANGES"

         Set objBCDEditCopyCmd = objShell.Exec("bcdedit /import C:\bcdbackup")

         WScript.Quit

    End If

    'Now copy the existing current BCD entry and determine the new GUID of the new BCD entry

    Set objBCDEditCopyCmd = objShell.Exec("bcdedit /copy " & objElement.ObjectID & " /d " & """" & strBootEntryName & """")

    Do While Not objBCDEditCopyCmd.StdOut.AtEndOfStream

         strCommandOutputReadLine = objBCDEditCopyCmd.StdOut.ReadLine()

         If Instr(strCommandOutputReadLine, "The entry was successfully copied to") <> 0 Then

              strNewLoaderGUID = Left(Right(strCommandOutputReadLine,39),38)

    End If

    Loop

    WScript.Echo "New Loader GUID = " & strNewLoaderGUID

    WScript.Echo

    If strNewLoaderGUID = "" Then

         WScript.Echo strComputer & vbTAB & "Could not Copy OS Loader."

         WScript.Quit

    End If

    'Popup dialog to get the location and name of the vhd

    'The BCD does not need a drive specification

    'We do not accept blank entries

    strVHDPath = InputBox ("Please provide the path, excluding drive letter and colon and file name of the VHD to boot from (use quotes if there are spaces). So if the path to the vhd is d:\win7.vhd just type \win7.vhd")

    If strVHDPath = "" then

         WScript.Echo "Blank path is not accepted for " & strNewLoaderGUID

         WScript.Echo "ROLLING BACK CHANGES"

         Set objBCDEditCopyCmd = objShell.Exec("bcdedit /import C:\bcdbackup")

         WScript.Quit

    End If

    'Ok now call the sub setBCDEdit and pass it the necessary parameters to make changes to the newly copied BCD entry

    Call setBCDEdit ("/set " & strNewLoaderGUID & " device vhd=[locate]" & strVHDPath, "device partition", strVHDPath)

    Call setBCDEdit ("/set " & strNewLoaderGUID & " osdevice vhd=[locate]" & strVHDPath, "osdevice partition", strVHDPath)

    Call setBCDEdit ("/set " & strNewLoaderGUID & " resumeobject " & strNewLoaderGUID, "resumeObject", strNewLoaderGUID)

    Call setBCDEdit ("/set " & strNewLoaderGUID & " detecthal on", "detecthal value", "on")

    Call setBCDEdit ("/set " & strNewLoaderGUID & " nx OptIn", "nx value", "optin")

    varDefaultLoader = MsgBox ("Do you want this to be the default booting OS?", vbYesNo)

    If varDefaultLoader = vbYes Then

         Call setBCDEdit ("/default " & strNewLoaderGUID, "boot loader", "Default")

    End If

    Wscript.Echo

    'This Sub accepts parameters from the calling line and inserts them into the subsequent bcdedit command line before running them

    Sub setBCDEdit (strCmdswitch, strSwitchDescription, strSwitchSetting)

    Set objBCDEditCopyCmd = objShell.Exec("bcdedit " & strCmdSwitch)

    If Instr(objBCDEditCopyCmd.StdOut.ReadAll,"The operation completed successfully.") <> 0 then

         WScript.Echo "The " & strSwitchDescription & " for the new Boot Loader " & strNewLoaderGUID & " was successfully set to " & strSwitchSetting

         WScript.Echo

    Else

         WScript.Echo "Failed to set the " & strSwitchDescription & " for the new Boot Loader to " & strSwitchSetting

         WScript.Echo "ROLLING BACK CHANGES"

         Set objBCDEditCopyCmd = objShell.Exec("bcdedit /import C:\bcdbackup")

         WScript.Quit

    End If

    End Sub

  • After Booting into DSRM DC takes a long time to become available

    I had an interesting issue recently and I thought it would be useful to share the information. I was working on a test environment was going through some DR scenarios and noticed that the DC’s were taking a long time to boot into normal mode after booting into DSRM. This was true even if I did nothing in DSRM other than logon and restart.

     

    DC’s were Windows 2003 SP2 x64

    Originally 2 DC’s in the environment, but the test was more pronounced with more DC’s (3 onwards, i.e. more replication partners)

    IPSec was utilized between DC’s (configured via group Policy; using Certificates, but the same issue was seen using Kerberos)

     

    After rebooting from DSRM the Domain Controller takes an unusual length of time (6-15 minutes from “Applying Network Settings” to the actual logon prompt; dependent on the number of DC’s in the environment) to enable logon at the console. “Applying Network Settings” takes approximately 5-6 minutes and when the logon dialog box appears, this usually does not display the logon domain for about another 5-10 minutes, by which time a user can logon. When carrying out the same procedure on x86 DC’s the timings were considerably reduced and I haven't had time for further investigation.

     

    Testing, Log Analysis and Research found this is an expected behaviour. This is the High Level theory of what is occurring.

    • IPSec is implemented using a Domain Based IPSec Policy using Certificates and this scenario plays the same if Kerberos authentication is used.
    • When a DC is booted into DSRM mode the Group Policy is cleared with the exception of IPSec Policy.
    • When restarting the DC into normal mode, the server waits for Initial synchronization. In this case, the initial synchronization will fail as IPSec initialises but the configuration is not applied fully, so the DC cannot have comms with partner DC’s.
    • As the Certificate details are stored in active directory, the Server can’t read it till the initial synchronization process is completed and the Directory Services have fully initialised locally (same case for Kerberos).
    • Also DNS cannot load up the Directory integrated zones due to lack of Directory Services availability, and therefore if the DC uses itself for DNS search this will fail.
    • Once the initial sync process completes (timesout), the domain controller can read the information from the local active directory, load up the DNS zone and get the certificate information.
    • IPsec can complete initialisation and the DC applies policy etc and life becomes good again.

    During testing I tried a number of things to resolve the issue, 3 of which worked (listed below), but all except item 2 were not satisfactory fixes for most production environments if you seriously need to IPSec between DC's.

    1. Disable the IPSec Policy.
    2. Use Pre-shared keys in the IPSec policy settings rather than Certificates or Kerberos.
    3. Set the registry value "Repl Perform Initial Synchronizations" to zero to prevent Initial Synchronisation http://support.microsoft.com/kb/305476. This obviously is not advised in a production environment, but did prevent the issue from occurring during testing.