Tuesday, February 28, 2012

PowerShell - Completing the configuration of a SysPrep'd SharePoint 2010

I recently created a template for spawning new development virtual machines (VM).  This template included SharePoint 2010 (SP2010).  I am using Virtual Machine Manager to generate my templates.  Software, such as SP2010, are sysprep’d as part of the process.

Since it is sysprep’d, then I’m going to need to finish the configuration once a new VM is spawned.  The great thing about SP2010 is that I can accomplish this with a PowerShell script.

Below is my configuration file and PowerShell script for making this happen.

---------- SharePointConfig.xml ----------

<Configuration>
  <Log Path="\\SomeDefinedLocationForLogs\SharePoint.log.txt" />
  <SharePoint>
    <Farm
        ConfigureEmail="true"
        FarmAccount="domain\username"
        FarmAccountPassword="password"
        DBServer="NEW_MSSQLSERVER"
        ConfigDB="SharePoint_Config"
        AdminContentDB="CentralAdmin_Content"
        Passphrase="password"
        EmailFromAddress="sharepoint@domain"
        EmailReplyToAddress="sharepoint@domain"
        SMTPServer="smtp.domain">
        <CentralAdmin Port="2007"/>
    </Farm>
  </SharePoint>
</Configuration>

------------------------------------------------
---------- SharePoint2010.ps1 ----------

    # Add SharePoint cmdlets reference
    Add-PSSnapin Microsoft.SharePoint.PowerShell
   
    $serverName = $env:computername

    $configFilepath = $args[0]
    $config = [xml](Get-Content $configFilepath)
    $logFile = $config.Configuration.Log.Path

try
{
    # Capture configuration into local variables
    $farmAcctUsr = $config.Configuration.SharePoint.Farm.FarmAccount
    $farmAcctPwd = $config.Configuration.SharePoint.Farm.FarmAccountPassword
    $configDB = $config.Configuration.SharePoint.Farm.ConfigDB
    $adminContentDB = $config.Configuration.SharePoint.Farm.AdminContentDB
    $configEmail = $config.Configuration.SharePoint.Farm.ConfigureEmail
    $smtp = $config.Configuration.SharePoint.Farm.SMTPServer
    $fromAddr = $cong.Configuration.SharePoint.Farm.EmailFromAddress
    $replyAddr = $config.Configuration.SharePoint.Farm.EmailReplyToAddress
    $passphrase = $config.Configuration.SharePoint.Farm.Passphrase
    $caPort = $config.Configuration.SharePoint.Farm.CentralAdmin.Port
   
    #Database Server (local?)
    $server = $serverName
    $server += "\"
    $server +=  $config.Configuration.SharePoint.Farm.DBServer
    
    # Create credential account object for Farm
    $farmAcct = New-Object System.Management.Automation.PSCredential $farmAcctUsr, (ConvertTo-SecureString $farmAcctPwd -AsPlainText -force)
   
    # Verify value of $passphrase variable
    if ($passphrase.Length)
    {
        $passphrase = (ConvertTo-SecureString $passphrase -AsPlainText -force)
    }
    else
    {
        $passphrase = $farmAcct.Password
    }
   
    # Create Farm
    New-SPConfigurationDatabase -DatabaseName $configDB -DatabaseServer $server -AdministrationContentDatabaseName $adminContentDB -Passphrase $passphrase -FarmCredentials $farmAcct
   
    # Verify farm creation
    $spFarm = Get-SPFarm -ErrorAction SilentlyContinue -ErrorVariable err
    if ($spfarm -eq $null)
    {
        throw "Unable to verify farm creation!"
    }
   
    # Secure SharePoint Resources
    Initialize-SPResourceSecurity
   
    # Install Services
    Install-SPService
   
    # Install Features
    Install-SPFeature -AllExistingFeatures
   
    # *** Install Central Admin ***
    # Lookup existing central admin
    $url = "http://$($serverName):$($caPort)"
    $sca = [Microsoft.SharePoint.Administration.SPWebApplication]::Lookup($url)
   
    # If no central admin, create one
    if ($sca -eq $null)
    {
        # Provisioning Central Administration
        New-SPCentralAdministration -Port $caPort -WindowsAuthProvider "NTLM"
       
        #Install Help
        Install-SPHelpCollection -All
       
        #Install Application Context
        Install-SPApplicationContent
    }
   
    # Configure Outgoing Email
    if ($configEmail -eq $true)
    {
        $cmd = "stsadm"
       
        $argList = " -o email"
        $argList += " -outsmtpserver `"$smtp`""
        $argList += " -fromaddress `"$fromAddr`""
        $argList += " -replytoaddress `"$replyAddr`""
        $argList += " -codepage 65001"
   
        # Execute the stsadm command for modifying email configuration.  Use the farmAcct as the account to execute under.
        start-process $cmd -ArgumentList $argList -Credential $farmAcct
    }
    else
    {
        # Log email skipped
    }
   
    echo ""
}
catch
{
    $err = $Error[0].Exception
    Write-Log $logFile $err 2
}

---------------------------------------------


PowerShell - Completing the configuration of a SysPrep'd Sql Server 2008

I recently created a template for spawning new development virtual machines (VM).  This template included SQL Server 2008.  I am using Virtual Machine Manager to generate my templates.  Software, such as SQL Server 2008, are sysprep’d as part of the process.

Since it is sysprep’d, then I’m going to need to finish the configuration once a new VM is spawned.  The great thing about SQL Server 2008 is that I can accomplish this with a PowerShell script.

Below is my configuration file and PowerShell script for making this happen.

---------- SqlServerConfig.xml ----------

<Configuration>
  <Log Path="\\SomeDefinedLocationForLogs\SqlServer.log.txt" />
  <SqlServer
    SqlSetupPath="\setup.exe"
   SqlInstallIso="\\SomeSharedDriveWithInstallMedia\ISOs\SQLServer\2008\R2\en_sql_server_2008_r2_developer_x86_x64_ia64_dvd_522665.iso"
    InstanceID="SQL_INSTANCEID"
    InstanceName="NEW_MSSQLSERVER"
    SqlSvcAccount="domain\username"
    SqlSvcPassword="password"
    RsSvcAccount="domain\username"
    RsSvcPassword="password"
    SqlSysadminAccounts="builtin\administrators"
    AgtSvcAccount="domain\username"
    AgtSvcPassword="password"
    AsSvcAccount="domain\username"
    AsSvcPassword="password"
    AsSysadminAccounts="builtin\administrators"
    IsSvcAccount="domain\username"
    IsSvcPassword="password"   
    SecurityMode="SQL"
    SaPassword="password"
    TcpEnabled="1"
    />
</Configuration>

------------------------------------------------
---------- SqlServerSetup.ps1 ----------

    $configFilepath = $args[0]

    $config = [xml](Get-Content $configFilepath)

    # Capture configuration into local variables
    $SqlSetupPath = $config.Configuration.SqlServer.SqlSetupPath
    $SqlIntallIso = $config.Configuration.SqlServer.SqlInstallIso
    $InstanceID = $config.Configuration.SqlServer.InstanceID
    $InstanceName = $config.Configuration.SqlServer.InstanceName
    $SqlSvcAccount = $config.Configuration.SqlServer.SqlSvcAccount
    $SqlSvcPassword = $config.Configuration.SqlServer.SqlSvcPassword
    $RsSvcAccount = $config.Configuration.SqlServer.RsSvcAccount
    $RsSvcPassword = $config.Configuration.SqlServer.RsSvcPassword
    $AsSvcAccount = $config.Configuration.SqlServer.AsSvcAccount
    $AsSvcPassword = $config.Configuration.SqlServer.AsSvcPassword
    $IsSvcAccount = $config.Configuration.SqlServer.IsSvcAccount
    $IsSvcPassword = $config.Configuration.SqlServer.IsSvcPassword      
    $AgtSvcAccount = $config.Configuration.SqlServer.AgtSvcAccount
    $AgtSvcPassword = $config.Configuration.SqlServer.AgtSvcPassword
    $SqlSysadminAccounts = $config.Configuration.SqlServer.SqlSysadminAccounts
    $AsSysadminAccounts = $config.Configuration.SqlServer.AsSysadminAccounts       
    $TcpEnabled = $config.Configuration.SqlServer.TcpEnabled

    # Mount SQL Server installation media
    $driveLetter = MountISO $SqlIntallIso
   
    # Build SQL Server installer executable string.
    $cmd = $driveLetter
    $cmd += $SqlSetupPath

    # Build  string with command-line arguments for SQL Server installer
    $arguments = " /q"
    $arguments += " /ACTION=CompleteImage"
    $arguments += " /INSTANCENAME=`"$InstanceName`""
    $arguments += " /INSTANCEID=`"$InstanceID`""
    $arguments += " /SQLSVCACCOUNT=`"$SqlSvcAccount`""
    $arguments += " /SQLSVCPASSWORD=`"$SqlSvcPassword`""   
    $arguments += " /RSSVCACCOUNT=`"$RsSvcAccount`""
    $arguments += " /RSSVCPASSWORD=`"$RsSvcPassword`""   
    $arguments += " /AGTSVCACCOUNT=`"$AgtSvcAccount`""
    $arguments += " /AGTSVCPASSWORD=`"$AgtSvcPassword`""     
    $arguments += " /SQLSYSADMINACCOUNTS=`"$SqlSysadminAccounts`""
    $arguments += " /IACCEPTSQLSERVERLICENSETERMS"
   
    # Combine arguments string with installer command to create a complete command-line executable statement
    $cmd += $arguments

    # Invoke the command
    invoke-expression $cmd

    # take pause to allow the install process to complete
    start-sleep -s 10
   
    # Unmount the ISO.  User doesn't need it "in the drive"
    UnmountISO

---------------------------------------------

Mounting ISOs with PowerShell

I made the below PowerShell functions for mounting and unmounting ISOs.  I used Virtual Clone Drive (VCD).  The downside, is it seems I can’t control which letter it assigns from the command line.  And the function below always unmounts any ISOs prior to mounting the new one.  At least that happens with the version of VCD I’ve been using.



# Mounts the specified ISO, and returns the Drive letter assigned to it
function MountISO([string] $fileNamePath)
{
    try
    {
        $cmd = "C:\VirtualCloneDrive\vcdmount.exe"
           
        Write-Log $logFile $cmd 0
        Write-Log $logFile $fileNamePath 0
       
        #invoke-expression $cmd
        start-process -FilePath $cmd -ArgumentList $fileNamePath
       
        # take pause to allow the mounting command to complete and the OS to do its part in making it available to the system.
        start-sleep -s 10

        # The newly mounted ISO is the last logical disk in the list.
        $psDrives = Get-WmiObject -Class Win32_LogicalDisk

        # The deviceID property represents the drive letter (i.e. "E:")   
        return $psDrives[$psDrives.Count - 1].DeviceID
    }
    catch
    {
        $err = $Error[0].Exception
        Write-Log $logFile $err 2
        return ""
    }
}

# Unmounts any ISOs still mounted.
function UnmountISO
{
    $cmd = "C:\VirtualCloneDrive\vcdmount.exe"
       
    $cmd += " /u /h /p"
           
    invoke-expression $cmd
}   

Wednesday, February 22, 2012

SqlSecureString

SqlSecureString - It’s a foolish little object type somewhere inside SQL Server that is a pain when it comes to PowerShell (PS).  One of the challenges of a PS script is being able to use passwords within the script.  Several times in creating a “CompleteImage” script for SQL Server 2008, I kept getting an error message that my password values are not of type SqlSecureString.

How did my team fix this?  By defining the password string variables as follows:

$newPwd = convertTo-Securestring “MyPassword” –AsPlainText –Force

Wednesday, February 8, 2012

Windows Server 2008 - Disk Cleanup

I installed a bunch of software (plus their update) as part of building out a Windows Server 2008 VM.  At the end of installation, I wanted to run the disk cleanup and reclaim some space.  Unfortunately it is not there for reasons only known to the Microsoft Overlords.

In order to correct this, I used the following link:


Tuesday, February 7, 2012

Native Boot VHD on USB

With my primary drive encrypted (thanks BitLocker) and my desire to be able to native boot a VHD, I went in search of a solution.

After much searching, I found a blog post: Native VHD Boot from an External USB.  I’ve decided to follow the directions and see if it works.

Below documents my deviations and distinctions to the process.

1)  Creating the VHD – I already had a baseline Windows 7 VHD.  So I made a copy to screw up.
2)  I did not change the locale information.  My VHD was already properly configured for all that stuff.
3)  Instead of using the INIFILE tool, I used my administrator command console to navigate to the folder, then executed “notepad <filename.ext>” in order to open it with administrative privileges.  This allows for the ability to save back.
      a)  For usbstor.inf and brfmcsto.inf, I went ahead and added the “; SERVICE_BOOT_START” to the end of the StartType value, and created a completed new entry at the end of the immediate list for LoadOrderGroup.
4)  So I did need PsExec (from PsTools), and executed from my administrator command console: “PsExec -i -d -s C:\Windows\System32\cmd.exe”
      a)  In the new command console, I ran the command “whoami” in order to see “nt authority/system
5)  Under the folder “R:\Windows\System32\DriverStore\FileRepository\” I had at least two of each folder with their own GUIDs.  So I deleted the PNF file in each.
6)  Under the folder “R:\Windows\winsxs\”, I had no PNF files to delete.
7)  I skipped creation of the differencing VHD cause I have base lines already, and really want the separation between them.

Now the process above continues to fail, because BitLocker has to go to the hard drive to get its security information, then unlock the bios, and then run the bootloader.  This is what consistently fails, and causes me the headaches of fixing my mistakes.

So I wondered, what if I partitioned my USB hard drive, and BitLocker’d the bootable partition?

Could I get a VHD on the encrypted partition to Native Boot?

Could the encrypted partition be used for boot loading, and Native Boot a VHD on the non-encrypted partition?

Apparently not.  Will update this posting if I can every get past this scenario.

Wednesday, February 1, 2012

When typing, every key appears as if SHIFT is being held down.

I ran into a problem where everything I typed appeared as if I was holding down the [SHIFT] key.  This of course was false, and cause all sorts of problems.  Even mouse clicks appeared as if I was holding down [SHIFT] prior to left clicking.

To remedy, I found a simple key stroke combination.

Holding down the right [SHIFT] key, I pressed the [CAPS LOCK] key.  After releasing both keys, everything appeared to be in good working order.

Windows 7 Ultimate - Virtual Hard Disk

Recently, I decided I needed to be smarter about building a more versatile virtual machine.  So I decided to go the route of creating a virtual hard disk (VHD).

With my main OS being Windows 7, I knew I had the ability to boot directly into a VHD.  Why?  Because this will allow the virtual to utilize my full system resources.

But what if I want to boot into my main OS?  Good news!  VHDs can be loaded by Virtual Box.  Meaning I can boot into my primary OS, and then use Virtual Box to launch a virtual machine (VM) into the VHD.

And there is even a third benefit.  Microsoft’s Hyper-V technology can take bootable VHDs and spawn VMs off.  Not only can I leverage the VHD locally (for those on-the-go and limited/weak network connectivity times), but teams could use it out of the cloud!

The first VHD I will create is an expandable Windows 7 (x64) Ultimate, 100GB baseline.  I will spell out my steps to creating it, making it accessible through Virtual Box, and finally direct booting into it.


The first part is to create a VHD.  I started by leveraging someone else’s blog: http://blog.tallan.com/2011/09/06/creating-a-bootable-vhd-that-can-also-be-run-from-a-virtual-machine/

The one deviation I made was creating an expandable VHD over a fixed one.  (Note: This is a baseline, I’m more worried about saving space over stealing a large chunk permanently away).  It was just a matter of adding the additional command line parameter type=expandable.


If you prefer other alternatives, Microsoft does have good resources for creating initial VHD files:  http://technet.microsoft.com/en-us/library/gg318052.aspx.

The blog goes on to talk about creating Server 2008, I’ll replicate many of the screen shots here for Windows 7 Ultimate.

* VM Name and OS Type

* Memory – The default is 512MB.  But with 8GB available, I decided to start with a “healthy” amount.

* Virtual Hard Disk

*** Virtual Machine Successfully Created ***

Now time to install the OS.

* Go into the settings of the new drive

* Choose Storage, and then select the CD drive.

* Find where you can setup the drive.  (Mine happens to be the disk image on the far right)

* Since I typically download ISOs, I have the ability to attach a virtual.  You can use your physical (Host) drive and insert a real disk.

*** Success!  We are now ready to install our OS ***

Since a billion people on the web have already documented how to install Windows 7, I’m just going to give you one link that you can follow: http://www.pcmag.com/article2/0,2817,2354687,00.asp

NOTE: I did run across the following error.
Failed to open a session for the virtual machine Windows 7 (x64) Ultimate.
VT-x features locked or unavailable in MSR. (VERR_VMX_MSR_LOCKED_OR_DISABLED).
Result Code:
E_FAIL (0x80004005)
Component:
Console
Interface:
IConsole {1968b7d3-e3bf-4ceb-99e0-cb7c913317bb}

And to solve, I went out and found the following help:


Now that I have this baseline VHD.  I think I’ll check out the SharePoint 2010 Easy Setup Script.  (See Chris Johnson’s blog entry for more details.)

I also liked reading the rest of the blog about using BCDEDIT to launch my machine directly into the VHD.