Archive | Windows

Creating an offline replica of our Windows Environment-Part 2

In part one I setup an offline DC with DHCP, DNS, AND and a Windows 7 VM. Next I wanted to create a copy of our exchange server offline. Easy, right? Just install exchange 2003 with DisasterRecovery mode (setup.exe /DisasterRecovery). Easy.

That is when the fun started.  I brought up one VM with Server 2003 R2 Enterprise, set the server name to the same as the production server EXCHANGESERVER01 (this is all offline so we are okay) and tried installing with the DisasterRecovery switch. Error.:

The component “Microsoft Exchange Messaging and Collaboration Services” cannot be assigned the action “Disaster Recovery” because: – The server object for this server (“EXCHANGESERVER01”) is a clustered Exchange Virtual Server . You may not perform maintenance on this object from a standalone server.  Cluster Admin should be used to perform maintenance on this Exchange Virtual Server.

Crap. Our production Exchange 2003 server is a cluster, and the installer found the AD record (offline) for the mail server. At least we know know what that the correct info is in AD! (note to self, look at EXCHDUMP to find the info that AD has). So I decided to try and trick Exchange 2003 that is being installed on a cluster, but with only one node.

So I added a couple of drives to the 2003 server (and a rename) and changed the ESX SCSI controller to “Virtual”. Tried to boot the VM and . . . Error. Crap.

Power On virtual machine:VMware ESX Server cannot open the virtual disk, “/vmfs/volumes/GUID/OEXCHANGESERVER01/EXCHANGESERVER01-02.vmdk” for clustering.  Please verify that the virtual disk was created using the ‘thick’ option.

Turns out you have to create the VMDK drives images from the command line if you want them to act like a cluster and be shared!

vmkfstools -d eagerzeroedthick -c 1G -a lsilogic EXCHANGESERVER01-02.vmdk

I recreated all my drives, and got the VM to boot. Next, I went into Cluster administrator and setup a simple 1 node cluster. Now it was time to try and install exchange 2003 AGAIN. I fired up Exchange’s setup.exe /DisasterRecovery, AGAIN. Error. Crap.

The component “Microsoft Exchange” cannot be assigned the action “Disaster Recovery” because: – Microsoft Exchange setup does not support the use of the DisasterRecovery action when running on cluster nodes.

Seems like I am in a loop. Can’t install exchange via DisasterRecovery on a cluster, and I can’t install exchange on a single machine that has an AD record of it being a cluster. Crap.

I decided to just go ahead and install exchange normally, no DisasterRecovery switch, on the one node cluster. While I went through the setup, I did some surfing. Turns out what I was trying to do is :

How to Move All Exchange Virtual Servers from a Production Exchange 2003 Cluster to a Standby Exchange 2003 Cluster.

Maybe it is just me, but that title does not sound like what I am trying to do – recover a cluster on a single  node. Anyway, I continued installing exchange 2003 normally, on my offline single node cluster. Applied service pack 2, and then added a couple of roles to the cluster acording to the article.

  1. Created an IP address and Network name on the cluster that matched the production server, and brought them online
  2. Created an Exchange System Attendant resource.

Once I created an Exchange System Attendant resource, the Cluster Administrator tool found the AD records and everything was matched up.  All the Exchange system configuration was there. ESM looked like it did on our production servers. Perfect. I borough one of our Information stores online (no data so it just creates a new one)

Now in theory, I can launch Outlook on my Windows  7 VM, and it should create a new mailbox in the right store (for this test, I did not try to restore our edbs – that is my next post.) .

Worked. Very nice.

 

 

Creating an offline replica of our Windows Environment-Part 1

I am working on my new project, upgrading our Exchange 2003 environment to Exchange 2010. I wanted to create an off line replica of our current environemnet. This is how I set about doing that

  • Before I started, I created a new workstation – Windows 7 on our ESX server. This machine is joined to the domain, but no one has ever logged on (deployed via SCCM OSD). I left it overnight to make sure it was in AD and AD had replicated.
  • Next I created a network in ESX that is not attached to any adapters (and no other machines are connected to) – called “offline network”
  • I then used the built in ESX function of cloning a VM, and created a clone of one of our Domain Controllers (DC).
  • This clone was assigned to the off line network, and the Windows 7 VM was moved to the offline network.
  • Since the DC has AD,DNS, and DHCP all running on it, I should be able to reboot the Windows 7 machine and I should be able to log in with my account.
  • Since I never have logged on to this Windows 7 VM before, I know it is not using a cached version of my account and the Windows 7 VM has to be communicating to the DC.
  • Finally, I seized the FSMO roles from the the other non existant DC (not on the offline network)

That gets me a functioning DC and a functioning DNS with a test Windows 7 machine and Office 2010.

Next up: Recovering an Exchange 2003 cluster to a single machine in an off line network.

My new PowerShell install script

I wanted to write a PowerShell script that can execute common activities involved in deploying software. We require signed PowerShell scripts, so it was not practical to rewrite the script for every piece of software. Instead, I moved the configuration to an XML file.

The first function below takes an object (pulled from the XML config file) that contains the uninstall information. The scenario would be that you want to uninstall a piece of software before you install something else. First I try to remove the software via WMI. If that fails, I lookup the uninstall string and use msiexe.exe to try and uninstall the software bassed on the GUID of the software.

function UninstallStep ($Step)
{
    $CurrentDisplayName= $Step.CurrentDisplayName.Value
    $CurrentVersion= $Step.CurrentVersion.Value
      gwmi win32_product -filter "Name like '%$CurrentDisplayName%'" | foreach {
        $InstalledVersion = $_.Version
        if ($InstalledVersion -ne  $CurrentVersion) {
            write-host "Trying to uninstall $CurrentDisplayName $InstalledVersion via WMI"
	        if ($_.uninstall().returnvalue -eq 0) { write-host "Successfully uninstalled $CurrentDisplayName $InstalledVersion via WMI" }
	        else {
                write-host "WMI uninstall retured an error, Trying to uninstall $CurrentDisplayName $InstalledVersion from registry entries"
	            if (-not(Test-Path ("Uninstall:"))){New-PSDrive -name Uninstall -psprovider registry -root HKEY_LOCAL_MACHINE\Software\Microsoft\Windows\CurrentVersion\Uninstall | Out-Null}
	            Get-ChildItem -Path Uninstall: | Where-Object -FilterScript { $_.GetValue("DisplayName") -like "*$CurrentDisplayName*"} | ForEach-Object -Process {
	                $CommandToRun = "msiexec"
	                $UNSTring = $_.GetValue("UninstallString").split("{")
	                $Parameters = "/X /Q {" + $UNSTring[1]
	                write-host "Running Command: " $CommandToRun $Parameters
	                Start-Process $CommandToRun $Parameters -wait -workingdirectory $WorkingDirectory | out-null
	            }
	            }
        }
    }
}

Second piece of code is a generic command to run script. This is basically just a wrapper for the Start-Process command. It can be used to run any command, but mostly I use this to start the msiexec.exe program with parameters. I can also use this command to start an setup.exe. Again, this is read from the config.xml. If there are arguments, then the second part of the conditional runs.

Function CommandToRunStep ($Step)
{
  if ($Step.Command.Value -ne "") {
    $Command = $Step.Command.Value
    if ($Step.Arguments.Value -ne "") {
      $Arguments = $Step.Arguments.Value
      write-host "Running Command: " "$Command" "$Arguments"
      Start-Process "$Command" -ArgumentList "$Arguments" -wait -workingdirectory $WorkingDirectory | out-null
    }
    Else {
      write-host "Running Command: " "$Command"
      Start-Process -FilePath "$Command" -wait -workingdirectory $WorkingDirectory | out-null
    }
  }
}

The final function takes a process name from the config xml file and kills it.

function KillStep ($Step)
{
    $ProcessName= ($Step.ProcessName.Value).split(".")[0]
    if ($ProcessName -ne "") {

    write-host "Killing: " "$ProcessName"
    Stop-Process -force -processname "$ProcessName" -ea SilentlyContinue
    }
}

The main part of this script loops through the xml file and call the correct function. The xml file can contain any number of the three types of functions above, and they are run in sequential order. This gives me the ability to create a “task sequence” in an xml file that will be run with a PowerShell script in an SCCM advertised program.

#  Main
$WorkingDirectory = Split-Path -parent $MyInvocation.MyCommand.Definition
[ xml ]$s = Get-Content $WorkingDirectory\Install.xml

foreach ($Step in $s.Install.Steps.Step)
{
switch ($Step.StepType.Value)
    {
    "UninstallOlderThan" {UninstallStep ($Step)}
    "CommandToRun" {  CommandToRunStep ($Step)}
    "KillProcess" { KillStep ($Step) }
    }
}

Updated
And an example of the XML file would be:

<Install>
<Steps>
  <Step>
	<StepType Value="UninstallOlderThan" />
	<CurrentDisplayName value="Apple Application Support" />
    	<CurrentVersion value="1.3.3" />
  </Step>
  <Step>
	<StepType Value="CommandToRun" />
	<Command Value="msiexec" />
	<Arguments Value="/i AppleApplicationSupport.msi /quiet /norestart" />
  </Step>
  <Step>
	<StepType Value="CommandToRun" />
	<Command Value="iTunesSetup.exe" />
	<Arguments Value="/quiet DESKTOP_SHORTCUTS=0" />
  </Step>
  <Step>
	<StepType Value="KillProcess" />
	<ProcessName Value="Process.exe" />
  </Step>
</Steps
</Install>

Simple vbscript (HTA) to install fonts via SCCM

We have a group of users that need the ability to install fonts (.ttf and .otf). They are not administrators for their machines, so we usually go down there and install the fonts using runas. Since advertised SCCM programs can run as system, I can write a script to copy the fonts into the fonts directory. If I mark the package as allow user to intereact and run as administrator, the script will pop for the user to pick the fonts they want to install. Here is my hta code that runs once a button is clicked:

	Set objShell = CreateObject("Shell.Application")
	Set objFolder = objShell.BrowseForFolder (0, "Install Fonts From (Source):", (0))
	If objFolder Is Nothing Then
		window.close
	Else
		Set objFolderItem = objFolder.Self
		objPath = objFolderItem.Path
	End If

	Set objFso = CreateObject("Scripting.FileSystemObject")
	Set objFolder = objFso.GetFolder(objPath)
	bolGotFonts = False
	For each objFile in objFolder.Files
		If objFolder.Files.Count > 0 Then
		  If lcase(objFso.GetExtensionName(objFile.Path))="ttf" OR lcase(objFso.GetExtensionName(objFile.Path))="otf" then
			bolGotFonts = True
			DataArea.InnerHTML = DataArea.InnerHTML & "<input type=""checkbox"" name=""" & objFile.Path & """>" & objFile.Path & "</input><br/>"
		  End if
		End If
	Next
	if bolGotFonts Then DataArea.InnerHTML = DataArea.InnerHTML & "<br/><input id=runbutton  class=""button"" type=""button"" value=""Install Font"" name=""run_button""

This code will popup a browse dialog and put the filenames found in the select directory into the HTA’s DataArea.innerHTML (DataArea is just a <div>) with a checkbox and button to initiate the copy of the files:

SUB InstallFont
    DIM colChkElem, strDriveName, objChkBox
    SET colChkElem = window.document.getElementsByTagName("input")
    FOR EACH objChkBox IN colChkElem
        IF objChkBox.Type = "checkbox" THEN
            IF objChkBox.checked THEN
                strFileName = objChkBox.name
                Set objShell = CreateObject("Shell.Application")
                Set objFolder = objShell.Namespace(FONTS)
                objFolder.CopyHere strFileName
            END IF
        END IF
    NEXT
    DataArea.InnerHTML = ""
END SUB

Seems to work!

Default MAPI profiles after using Office Customization Tool

We are rolling out a new windows 7 desktop (via sccm task sequence) and one of the packages is the newly released Office 2010. I have been using OCT to modify the outlook profile via a PRF file. We want to take advantage of “cached mode” so that is one of the setting I use the PRF and OCT to set. What if I wanted to change the behavior of one machine, so that it does not use “cached mode”? Do I have to re-install office?

Seems that the default mapi profile is created by importing the PRF file. In windows 7, this registry setting can be found here:

HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Office\14.0\User Settings\{GUID}\Create\Software\Microsoft\Office\14.0\Outlook\Setup\ImportPRF

And in Windows 2008 R2 X64 it is here:

HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\Microsoft\Office\14.0\User Settings\{GUID}\Create\Software\Microsoft\Office\14.0\Outlook\Setup\ImportPRF

Just have this registry key (using 8.3 names) point to a PRF with different settings and the new MAPI profiles will use this PRF.

Powershell signing, makecert.exe and group policy – Part 2

In the previous post I showed how to create self signed signing certs via the makecert tool. Once I made the pfx files, and signed my script, I need to make the server I was working see the certificate chain. I created a GPO and applied it to the server OU. I added the Root cert to Trusted Root Certification Authorities, and I added the Cert to Trusted Publishers Certificates. Now when I open a signed script on a server, I no longer have to change the Set-ExecutionPolicy RemoteSigned and add the network path to the Local Intranet zone.

Powershell signing, makecert.exe and group policy – Part 1

We don’t have a pki infrastructure at my current job, and I like to keep my powershell scripts on my network drive. So I needed a way to run my .ps1 files off a network drive. Setting Set-ExecutionPolciy to Unrestricted seemed like a bad idea. So I looked into using a signed script and setting Set-ExecutionPolicy to RemoteSigned. I could not justify $300 to buy a trused third party cert, so I looked at makecert.exe from the Windows SDK.

First step make the Root CA cert, private key, and pxf.

  1. makecert -n “CN=RootName” -a sha1 -eku 1.3.6.1.5.5.7.3.3 -r -sv Root.pvk Root.cer
  2. pvk2pfx.exe -pvk Root.pvk -spc Root.cer -pfx Root.pfx -pi password

makecert and pvk2pfx can be found in the Windows SDK or in the Visual Studio bin directory. First command results in a private key (.pvk) and a certificate (.cer). Second command makes a pfx out of the first 2.

Second step is to create a certificate from the root cert above.

  1. makecert -pe -n “CN=Certificate” -a sha1 -eku 1.3.6.1.5.5.7.3.3 -ic Root.cer -iv Root.pvk -sv Certificate.pvk Certificate.cer
  2. pvk2pfx.exe -pvk Certificate.pvk -spc Certificate.cer -pfx Certificate.pfx -pi password

Now we are ready to sign our powershell script.

  1. $cert = Get-PfxCertificate Certificate.pfx
  2. Set-AuthenticodeSignature -Filepath script.ps1 -Cert $cert

Probably can be combined into one line, but I am not a guru yet.

Next post will talk about how I added this certs above to all the servers.

PowerShell is great

Okay, I drank the Kool-Aid. PowerShell is awesome. The first thing I wanted to do was make sure my $profile is always the most current no matter what machine was on. In the past I had a batch file that I would run that would open my command prompt the way I wanted. I modified that so if PowerShell is installed, then get my profile up-to-date and then launch PowerShell. This is part of my current batch file:

IF NOT EXIST "c:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe" GOTO NO_PS
powershell -NoProfile $a=(Split-Path $profile -parent);if (!(Test-Path $a)) {New-Item $a -type directory};
powershell -NoProfile if (!(Test-Path $profile)) {Copy-Item %~dp0Scripts\Microsoft.PowerShell_profile.ps1 (Split-Path $profile -parent)}
powershell -NoProfile if (!(Compare-Object $(Get-Content $profile) $(Get-Content Scripts\Microsoft.PowerShell_profile.ps1)).Count -eq 0 ) {Copy-Item Scripts\Microsoft.PowerShell_profile.ps1 (Split-Path $profile -parent)}
@start  %COMSPEC% /K PowerShell -nologo

This will copy over my profile if it does not exist or is a different version. Now when I am on a server with PowerShell I can double click my batch file and my environment is up to date!

Install Windows 7 RSAT Silently and Activate All Features

  • wusa /quiet /norestart amd64fre_GRMRSATX_MSU.msu
  • ocsetup RemoteServerAdministrationTools;RemoteServerAdministrationTools-ServerManager;RemoteServerAdministrationTools-Roles;RemoteServerAdministrationTools-Roles-CertificateServices;RemoteServerAdministrationTools-Roles-CertificateServices-CA;RemoteServerAdministrationTools-Roles-CertificateServices-OnlineResponder;RemoteServerAdministrationTools-Roles-AD;RemoteServerAdministrationTools-Roles-AD-DS;RemoteServerAdministrationTools-Roles-AD-DS-SnapIns;RemoteServerAdministrationTools-Roles-AD-DS-AdministrativeCenter;RemoteServerAdministrationTools-Roles-AD-DS-NIS;RemoteServerAdministrationTools-Roles-AD-LDS;RemoteServerAdministrationTools-Roles-AD-Powershell;RemoteServerAdministrationTools-Roles-DHCP;RemoteServerAdministrationTools-Roles-DNS;RemoteServerAdministrationTools-Roles-FileServices;RemoteServerAdministrationTools-Roles-FileServices-Dfs;RemoteServerAdministrationTools-Roles-FileServices-Fsrm;RemoteServerAdministrationTools-Roles-FileServices-StorageMgmt;RemoteServerAdministrationTools-Roles-HyperV;RemoteServerAdministrationTools-Roles-RDS;RemoteServerAdministrationTools-Features;RemoteServerAdministrationTools-Features-BitLocker;RemoteServerAdministrationTools-Features-Clustering;RemoteServerAdministrationTools-Features-GP;RemoteServerAdministrationTools-Features-LoadBalancing;RemoteServerAdministrationTools-Features-SmtpServer;RemoteServerAdministrationTools-Features-StorageExplorer;RemoteServerAdministrationTools-Features-StorageManager;RemoteServerAdministrationTools-Features-Wsrm