Monday, August 27, 2012

Java Generics and XML

In my world of .Net, I know exactly how to serialize and deserialize between Objects and an XML file.  I typically carry around functions with Generics so I can minimize my code.

I had to do something in Java, and struggled to find the exact same thing out on the web.  So I made my own set of functions using JAXB.

**************************************************

import java.io.*;

import javax.xml.*;
import javax.xml.bind.*;
import javax.xml.validation.*;

import org.xml.sax.SAXException;

/**
 * Generic Utilitarian Class 
 *
 */
public class GenericUtilities
{
/**
* Class for taking an XML file and converting it to define objects
* @param xmlFile - A valid XML file for pulling object definitions from
* @param schemaFile - 
* @param type - The class of the object to convert the XML into
* @return - The instance of the defined class, constructed from the XML
* @throws JAXBException 
* @throws FileNotFoundException
* @throws SAXException 
*/
public static T DeserializeObjects(String xmlFile, String schemaFile, Class type) throws JAXBException, FileNotFoundException, SAXException
{
// Create a JAXB context passing in the class of the object we want to marshal/unmarshal
        JAXBContext context = JAXBContext.newInstance(type);
 
// Create the unmarshaller to transform the XML back into an object
        Unmarshaller unmarshaller = context.createUnmarshaller();
 
        // Create a variable for the schema.
        Schema xmlSchema = null;
        
        if (schemaFile != null)
        {
        // Establish the schema factory for loading the schema
        SchemaFactory factory = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
       
        // Get a File object representation of the inputed schema
        File sf = new File(schemaFile);
       
        // Use the factory and file objects to instantiate an instance of the Schema object
        xmlSchema = factory.newSchema(sf);
        }
        
        // Set schema validation.  Null value turns validation off.
        unmarshaller.setSchema(xmlSchema);
        
        // Unmarshal the XML in the stringWriter back into an object
        Object o = unmarshaller.unmarshal(new FileInputStream(xmlFile));
        T entity = type.cast(o);
        
return entity;
}
/**
* Converts an instance object into a String containing the XML representation of that object.
* @param entity - The object to serialize into XML
* @return
* @throws JAXBException
*/
public static String SerializeObjects(T entity) throws JAXBException
{
// Create a JAXB context passing in the class of the object we want to marshal/unmarshal
        JAXBContext context = JAXBContext.newInstance(entity.getClass());
 
        // Create the marshaller, this will transform the object into XML
        Marshaller marshaller = context.createMarshaller();
 
        // Create a stringWriter to hold the XML
        StringWriter stringWriter = new StringWriter();
        
        // Marshal the javaObject and write the XML to the stringWriter
        marshaller.marshal(entity, stringWriter);
 
        return stringWriter.toString();
}
}

Monday, April 2, 2012

PowerShell - SQL Server 2008 Configuration

In an effort to continue to improve the quality of my code while expounding into new areas, I am now revisiting SQL Server 2008.

Previously, I created a PowerShell script to handle finishing the completion of a sysprep'd installation.  Now, I want to change my script logic to be more generic, and to include adding/installing the Analysis Service (SSAS) feature.

Below will be the script code followed by configuration xml snippet (I like to drive things via configurable XML file).  At the end of this post, you will see links tied back to the past post, and other related ones as well.

*************************
***** PowerShell Script *****
*************************
$configFilepath = $args[0]

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

$lvlInfo = 0
$lvlWarn = 1
$lvlError = 2

Write-Log $logFile "Begin SQL Server Setup" $lvlInfo

# Capture configuration into local variables
$argStrs = $ssCfg.Installation.ArgumentString
$properties = $ssCfg.InstallationProperty

$SqlSetupPath = $ssCfg.Installation.Installer
$SqlInstallIso = $ssCfg.Installation.ISO

$InstanceID = $properties | Where {$_.name -eq "instanceId"} | %{$_.value}
$InstanceName = $properties | Where {$_.name -eq "instanceName"} | %{$_.value}
$userName = $properties | Where {$_.name -eq "username"} | %{$_.value}
$password = $properties | Where {$_.name -eq "password"} | %{$_.value}

$propArray = $properties | Sort {$_.index} | %{$_.value}

Write-Log $logFile "Mount SQL Server installation media ($($SqlInstallIso))" $lvlInfo
$driveLetter = MountISO $SqlInstallIso

# Build foundation for SQL Server installer executable string.
$cmd = $driveLetter
$cmd += $SqlSetupPath

# Execute each installation action
foreach($cmdArgs in $argStrs)
{
    $expr = $cmd + ([string]::Format($cmdArgs, $propArray))

    Write-Log $logFile "Invoke expression: $($expr)" $lvlInfo

    invoke-expression $expr | Out-Null

    # take pause to ensure install process is complete
    start-sleep -s 10
}

# We are done with the installation media.  Time to unmount.
UnmountISO

# *** Address the SQLBrowser service ***
Write-Log $logFile "Configure SQLBrowser service." $lvlInfo

# Capture the name of the server
$serverName = $env:computername

#Retrieve the WMI object presentation of the service
$service Get-WMIObject win32_service -ComputerName $serverName -filter "name='SQLBrowser'"

# Try stopping the service
try
{
    $service.StopService()
}
catch
{
    Write-Log $logFile $Error[0] $lvlWarn
}

$service Get-WMIObject win32_service -ComputerName $serverName -filter "name='SQLBrowser'"
# Modify the log on credentials for the service.
$service.Change($null, $null, $null, $null, $null, $null, $userName, $password, $null, $null, $null)

# Try starting the service
try
{
    $service.StartService()

    # Give the service a moment to ensure completely started.
    start-sleep -s 10
}
catch
{
    Write-Log $logFile $Error[0] $lvlWarn
}


*************************
***** Configuration File *****
*************************
<Configuration>
    <Log Path="c:\windows\temp\install.log.txt" />
    <SqlServer>
        <Installation ISO="\\server\folder\en_sql_server_2008_r2_developer_x86_x64_ia64_dvd_522665.iso" Installer="\setup.exe">
        <ArgumentString> /q /ACTION=CompleteImage /INSTANCEID="{0}" /INSTANCENAME="{1}" /SQLSVCACCOUNT="{2}" /SQLSVCPASSWORD="{3}" /RSSVCACCOUNT={2}" /RSSVCPASSWORD="{3}" /AGTSVCACCOUNT="{2}" /AGTSVCPASSWORD="{3}" /SQLSYSADMINACCOUNTS="{4}" /IACCEPTSQLSERVERLICENSETERMS</ArgumentString>
        <ArgumentString> /q /ACTION=Install  /INSTANCEID="{0}" /INSTANCENAME="{1}" /FEATURES=AS /ASSVCACCOUNT="{2}" /ASSVCPASSWORD="{3}" /ASSYSADMINACCOUNTS="{4}" /IACCEPTSQLSERVERLICENSETERMS</ArgumentString>
        <Property index="0" name="instanceId" value="SQL_INSTANCEID" />
        <Property index="1" name="instanceName" value="NEW_MSSQLSERVER" />
        <Property index="2" name="username" value="domain\username" />
        <Property index="3" name="password" value="complexpassword" />
        <Property index="4" name="sysadminaccounts" value="builtin\administrators" />
        </Installation>
    </SqlServer>
</Configuration>

*************************
*************************

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

Wednesday, March 21, 2012

PowerShell - Report Server Integration - Part 4

The final part of the process is to activate the Report Server Integration feature for all existing site collections.  In previous posts we configured SS2008 and SP2010.  Outside the scope of these posts, my automation process has created some base sites outside of central administration.  Because our changes apply for all new sites, the existing sites do not have access to the Report Server feature.

Microsoft tells you how to do this manually per site: http://msdn.microsoft.com/en-us/library/bb677366.aspx.

I, on the other hand, want to do this via PowerShell as a last step in auto-configuration of Report Server Integration.

First, I have a little utility function for activating features within SharePoint.

# #######################
# ##### Utility Functions #####
# #######################
# A generic function for enabling a SharePoint feature
function ActivateFeature()
{
    # Required parameters for enabling a feature
    param([string]$Identity = $(throw "-Identity parameter is required"), [string]$Url = $(throw "-Url parameter is required"))

    Enable-SPFeature -Identity $Identity -Url $Url
}
# #######################
# #######################

Second, I use that function during the process of looping through site collections on the farm.

# ############################################
# ##### Activate Report Server Integration Feature #####
# ############################################

try
{
    # Capture array of Farm SPService objects into a local variable
    $webAppSrvs = (Get-SPFarm).Services

    # Loop through each SPService object
    foreach($webService in $webAppSrvs)
    {
        Write-Log $logFile "Web Service ($($webService.TypeName)) WebApp.Cnt: $($webService.WebApplications.Count)" $lvlInfo

        # Loop through each SPWebApplication object that exists under the current SPService object.
        foreach($webApp in $webService.WebApplications)
        {
            Write-Log $logFile "Web App: $($webApp.name); Feat Cnt: $($webApp.Features.Count); Prop Cnt: $($webApp.Properties.Count)" $lvlInfo

            # Check for any SPSiteCollection objects under the SPWebApplication object
            if ($webApp.Sites.Count -gt 0)
            {
                # Loop through each SPSiteCollection object
                foreach($siteColl in $webApp.Sites)
                {
                    # Capture the current state of access denied exception handling.
                    $accessDenied = $siteColl.CatchAccessDeniedException

                    # Prevent access denied exceptions from being swallowed.
                    $siteColl.CatchAccessDeniedException = $false

                    # Activate features related to Report Server Integration
                    foreach($feature in $features)
                    {
                        try
                        {
                           ActivateFeature -Identity $feature.Identity -Url "$($siteColl.Url)"
                        }
                        catch
                        {
                            Write-Log $logFile $Error[0] $lvlWarn
                        }
                    }

                    # Return the access deniend exception handling to previous state
                    $siteColl.CatchAccessDeniedException = $accessDenied
                }
            }
        }
    }

    Write-Log $logFile "Successfully activated Report Server Integration feature on all existing site collections." $lvlInfo
}
catch
{
    Write-Log $logFile $Error[0] $lvlError
}




********************
PowerShell - Report Server Integration - Part 1
PowerShell - Report Server Integration - Part 2
PowerShell - Report Server Integration - Part 3
********************

PowerShell - Report Server Integration - Part 3

After configuring SS2008, SP2010 needs to have its Report Server Integration configured.  The manual process takes care of both defining the configuration and activating the feature.

To manually do so, read http://msdn.microsoft.com/en-us/library/bb326213.aspx.

Because the manual process really takes two separate actions, this post will focus on configuring SP2010 Report Server Integration.  The next post will focus on activating the Report Server Integration feature in existing site collections.

The script below will perform the following steps:
1)  Find the existing configuration and delete it.
2)  Create the new configuration, defining its properties
3)  Apply the new configuration to the farm.

# ###############################################
# ##### Configure SharePoint Report Server Integration #####
# ###############################################

try
{
    Write-Log $logFile "Being configuration of SharePoint 2010 Report Server Integration" $lvlInfo

    # Load the library that contains the SPRSSServiceConfiguration class.
    [Reflection.Assembly]::LoadWithPartialName(“Microsoft.ReportingServices.SharePoint.Common”) | Out-Null

    # Retrieve the name of the service from a string constant.  Placed in a local variable for “short-hand” use later
    $serviceName = [Microsoft.ReportingServices.SharePoint.Common.SPRSServiceConfiguration]::RSServiceName

    # Get the current SharePoint farm.
    $spFarm = Get-SPFarm

    # Find all SPService objects that are of the SPRSServiceConfiguration type
    $rsServiceList = [array] $spFarm.Services | Where {$_.TypeName -eq "Microsoft.ReportingServices.SharePoint.Common.SPRSServiceConfiguration"}

    if($rsServiceList -ne $null)
    {
        # Delete any found SPService objects.  We will be making a new one for our configuration purproses
        $rsServiceList | %{$_.Delete()}
    }
       
    # Generate a new SPRSServiceConfiguration object for the Report Server Integration on the SharePoint farm.
    $ssrs = new-object -typeName Microsoft.ReportingServices.SharePoint.Common.SPRSServiceConfiguration($serviceName, $spFarm)

    # Set the configuration properties to work with the SQL Server configuration
    $ssrs.RSServerUrl = $ssrsURL
    $ssrs.AuthenticationType = $authType

    # Execute update functions to commit new configuration
    $ssrs.update() | Out-Null
    $ssrs.provision() | Out-Null

    # Provide the SQL Server Reporting Services service account with SharePoint access
    Get-SPWebApplication -IncludeCentralAdministration | %{$_.GrantAccessToProcessIdentity($sqlServiceAcct)}

    Write-Log $logFile "Successfully configured SharePoint 2010 Report Server Integration" $lvlInfo
}
catch
{
    Write-Log $logFile $Error[0] $lvlError
}



********************
PowerShell - Report Server Integration - Part 1
PowerShell - Report Server Integration - Part 2
PowerShell - Report Server Integration - Part 4
********************

PowerShell - Report Server Integration - Part 2

The first part in establishing Reporting Services is to configure SS2008.  Between cmdlets and WMI objects, this script became a success.  (Catch up on your SS2008 PowerShell reading here).

You can read all about how to manually use the management tools to do this at http://msdn.microsoft.com/en-us/library/bb283151.aspx.

In order to repeat this process through PowerShell, my script does the following steps:
1)  Generate and execute a new database SQL script.
2)  Generate and execute a permissions SQL script.
3)  Connect SS2008 Reporting Services to the new Report Server database.
4)  Stop, then Start the SS2008 Reporting Services service.

Note:  The "Get-WmiObject" cmdlet seemed to give me some fits when it was all lower cased (instead of the camel case at the beginning of this note).

# #######################################################
# ##### Configure SQL Server Report Server - PowerShell Script #####
# #######################################################

try
{
    Write-Log $logFile "Being configuration of SQL Server 2008 Reporting Services" $lvlInfo

    # Capture the wmi namespace property into a local variable
    $wmiNameSpace = "root\Microsoft\SqlServer\ReportServer"

    # Retrieve the ReportServer namespace definition object
    $nsDef = Get-WmiObject -ComputerName $serverName -class "__namespace" -namespace $wmiNameSpace -ErrorAction SilentlyContinue
   
    # Check for existence of namespace definition object
    if ($nsDef -eq $null)
    {
        throw "Failed to find the local Report Server."
    }
   
    # Expand the wmi namespace property to include the specific Report Server instance
    $wmiNameSpace = "$($wmiNameSpace)\$($nsDef.name)\v10\Admin"
   
    # Retrieve the MSReportServer_ConfigurationSetting object
    $rsConfig = Get-WmiObject -ComputerName $serverName -class "MSReportServer_ConfigurationSetting" -namespace $wmiNameSpace -ErrorAction SilentlyContinue
   
    # Check that the object was successfully retrieved
    if ($rsConfig -eq $null)
    {
        throw "Failed to obtain Report Server configuration object."
    }

    # Create the SQL script that will generate a new ReportServer database
    $methResult = $rsConfig.GenerateDatabaseCreationScript($reportServer, $lcid, $true)

    # Check the success of creating the SQL script
    if ($methresult["HRESULT"] -ne 0)
    {
        throw "Failed to create the SQL Script for generating a new report server database."
    }

    # Set the local SQL Server as the base location for executing SQL scripts
    Set-Location SQLSERVER:\SQL\$serverName\$instName

    # Execute the SQL Script for creating a new Report Server
    invoke-sqlcmd -Query $methResult["Script"] -Username $sqlUser -Password $sqlPwd -OutputSqlErrors $true | Out-Null
   
    # Set the base location back to the original command prompt for executing scripts
    Set-Location $currentLocation

    # Create a SQL Script that provides the service account access to the newly created database
    $methResult = $rsconfig.GenerateDatabaseRightsScript($sqlServiceAcct, $reportServer, $false, $true)

    # Check the success of creating the SQL script
    if ($methresult["HRESULT"] -ne 0)
    {
        throw "Failed to create the SQL Script for providing access for the service account to the new report server database"
    }

    # Set the local SQL Server as the base location for executing SQL scripts
    Set-Location SQLSERVER:\SQL\$serverName\$instName

    # Execute the SQL Script for providing the service account access to the new Report Server
    invoke-sqlcmd -Query $methResult["Script"] -Username $sqlUser -Password $sqlPwd -OutputSqlErrors $true | Out-Null

    # Set the base location back to the original command prompt for executing scripts
    Set-Location $currentLocation

    # Set the SQL Server Reporting Services to use the newly created Report Server
    $methResult = $rsConfig.SetDatabaseConnection($dbServer, $reportServer, $credType, $sqlUser, $sqlPwd)

    # Check the success of setting the database connection
    if ($methresult["HRESULT"] -ne 0)
    {
        throw "Failed to change SQL Server's Reporting Services to use the newly created Report Server"
    }

    # Stop and Start SQL Server's Reporting Services to ensure changes take affect
    $rsconfig.SetServiceState($false, $false, $false)
    $rsconfig.SetServiceState($true, $true, $true)

    # With the restarting of the service, recapture the WMI object and confirm changes
    $rsConfig = Get-WmiObject -ComputerName $serverName -class "MSReportServer_ConfigurationSetting" -namespace $wmiNameSpace -ErrorAction SilentlyContinue

    # Check that the SQL Server Reporting Services is ready for SharePoint integration.
    if ($rsConfig.IsSharePointIntegrated -eq $false)
    {
        throw "The SQL Server Reporting Services Report Server database is not configured for SharePoint integration."
    }

    Write-Log $logFile "Successfully Configured SQL Server with a new Report Server and is ready for SharePoint integration." $lvlInfo
}
catch
{
    Write-Log $logFile $Error[0] $lvlError
}

# Ensure the return of the current location back to local system.
Set-Location $currentLocation



********************
PowerShell - Report Server Integration - Part 1
PowerShell - Report Server Integration - Part 3
PowerShell - Report Server Integration - Part 4
********************

PowerShell - Report Server Integration - Part 1

I have SQL Server 2008 (SS2008) and SharePoint 2010 (SP2010).  They are ingrained in a Virtual Machine (VM) template.  I've already had PowerShell (PS) scripts that run against these sysprep'd software applications.  Base configuration has occurred.  Now I want a PS script to establish the Report Server Integration feature within SP2010.

In order to accomplish this, I have to configure Reporting Services within SS2008, Report Server Integration in SP2010, and then activate the features for all existing site collections within SP2010.

The next several posts will describe the break out of each part of the script.  Below is a note about header information and the variables I used throughout the script code you'll see in future posts.

********************
PowerShell - Report Server Integration - Part 2
PowerShell - Report Server Integration - Part 3
PowerShell - Report Server Integration - Part 4
********************

PowerShell Snapins needed.  The use of IF blocks is because my script is daisy chained with others, and I don't know what has been previously loaded, and what still needs to be.

# Add Sql Server cmdlets references
if((Get-PSSnapin | Where {$_.Name -eq "SqlServerCmdletSnapin100"}) -eq $null)
{ Add-PSSnapin SqlServerCmdletSnapin100; }
if((Get-PSSnapin | Where {$_.Name -eq "SqlServerProviderSnapin100"}) -eq $null)
{ Add-PSSnapin SqlServerProviderSnapin100; }

# Add SharePoint cmdlets reference
if((Get-PSSnapin | Where {$_.Name -eq "Microsoft.SharePoint.PowerShell"}) -eq $null)
{ Add-PSSnapin Microsoft.SharePoint.PowerShell; }


The variety of local variables I use through out the scripts are:

# Capture the current directory where the script is executing from
$currentLocation = Get-Location

# Capture the name of the computer.
$serverName = $env:computername

# Load the XML configuration file
$config = [xml](Get-Content $args[0]) 

# Define logging variables
$lvlInfo = 0
$lvlWarn = 1
$lvlError = 2

# Get the log file fully qualified path and name.
$logFile = $config.Configuration.Log.Path

# Get the section with configuration for Reporting Services
$rsCfg = $config.Configuration.ReportingServices

$instName = $rsCfg.InstanceName     # Server Instance Name
$reportServer = $rsCfg.ReportServer.DatabaseName    # Name of new Report Server database
$lcid = [int]$rsCfg.ReportServer.Lcid    # Language definition id.  1033 for english


#  Administrative account with permission to execute changes in SharePoint
$sqlUser = $rsCfg.Administrator.Username
$sqlPwd = $rsCfg.Administrator.Password

$sqlServiceAcct = $rsCfg.ReportServer.ServiceAccount    # Service account to associate with Sql Server reporting services
$credType = [int]$rsCfg.ReportServer.CredentialType     # CredentialsType  (0 - Windows; 1 - SQL Server; 2 - Service)
$authType = $rsCfg.AuthenticationType    # How SharePoint will connect with Sql Server Report Server (Windows, Trusted)
$features = $rsCfg.Feature    # Features within SharePoint site collections that will need activating for Reporting Services to work  (Reporting, ReportServer)

$dbServer = "$serverName\$instName"    # Server and instance identifier for use with SQL Server Report Server Configuration
$ssrsURL = "http://$serverName/$($reportServer)_$instName"    # The URL of the report server used by SharePoint for integration.

Monday, March 5, 2012

PowerShell - Simple URL Check

I wanted a basic function that checks that a URL is valid.  So I created the following:

function IsValidUrl([string] $url)
{
    # Declare default return value
    $isValid = $false
   
    try
    {
        # Create a request object to "ping" the URL
        $request = [System.Net.WebRequest]::Create($url)
        $request.Method = "HEAD"
        $request.UseDefaultCredentials = $true

        # Capture the response from the "ping"
        $response = $request.GetResponse()
        $httpStatus = $response.StatusCode
   
        # Check the status code to see if the URL is valid
        $isValid = ($httpStatus -eq "OK")
    }
    catch
    {
        # Write error log
        Write-Host $Error[0].Exception
    }
   
    return $isValid
}

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.