PowerCLI: Linked VMs Refresh

April 1, 2015 – 5:19 pm

And here is the follow up script for the pre-prod environment. This script can be scheduled to run daily to provide a fresh pre-production or test environment when your team arrives in the office.

#=============================================================================
# Script       : Pre-Production environment refresh
# Author       : Marc Bouchard
# Credits      : Rob Girard
# Version      : 1.0
# Revision     : 03/31/2015
# Purpose      : Refresh non-persistent copies of production VMs
# Requirements : 
# 1. Source VM snapshot is created
# 2. New VM is created, configured to use the base virtual HD of the source VM.
#    This drive is configured as a non-persistent disk to prevent corruption of
#    the reference image. This basically makes a point in time reference image
#    for testing changes to the production environment.
# 3. Both VMs must be powered on
#
# Parameters   : List of source (production) VMs
#=============================================================================

#=============================================================================
# --- Command line parameters
#=============================================================================

param (
	[Parameter(Mandatory=$true)]
	[String[]]$ListOfVMs = $(throw "List of source (production) VMs required")
)

#=============================================================================
# Screen setup
#=============================================================================

# set regular console colours
[console]::backgroundcolor = "black"
[console]::foregroundcolor = "white"

# clear screen
clear-host

#=============================================================================
# --- Load Powershell Snap-ins
#=============================================================================

if (!(get-pssnapin -name VMware.VimAutomation.Core -erroraction 'SilentlyContinue')) {
	Write-Host "[INFO] Adding PowerCLI Snapin"
	add-pssnapin VMware.VimAutomation.Core -ErrorAction 'SilentlyContinue'
	if (!(get-pssnapin -name VMware.VimAutomation.Core -erroraction 'SilentlyContinue')) {
		Write-Host "[ERROR] PowerCLI snapin NOT added" -ForegroundColor Red
	} Else {
		Write-Host "[INFO] PowerCLI snapin added"
	}
}

#=============================================================================
# --- CUSTOMIZE THIS SECTION ONLY FOR YOUR ENVIRONMENT
#=============================================================================

$DateOnly = Get-Date -f yyyy-MM-dd
$ScriptLocation="C:\Scripts\PreProd\"
$CredUser = "ServiceAccountUserID"
$CredFile = $ScriptLocation + "Refresh-PreProd.crd"
$ReptFile = $ScriptLocation + "Refresh-PreProd_" + $DateOnly + ".log"
$vCenter = "vCenterServerName"
$Prefix = "PRE_"

#SMTP relay
$smtpServer = "smtp.companyname.com"
$Sender = "refresh-preprod@companyname.com"
$Recipient = "sysadmin@companyname"

#=============================================================================
# --- FUNCTION: Send e-mail
#=============================================================================

function SendMail{
   Write-Host "[INFO]    Sending e-mail"

	#Creating a Mail object
	$msg = new-object Net.Mail.MailMessage

	#Creating email attachment object
	$att = new-object Net.Mail.Attachment($ReptFile)

	#Creating SMTP server object
	$smtp = new-object Net.Mail.SmtpClient($smtpServer)

	#Email structure 
	$msg.From = $Sender
	$msg.To.Add($Recipient)
	$msg.subject = "Pre-production environment refresh " + $DateOnly
	$msg.body = "Please see attached file."
	$msg.Attachments.Add($att)
	
	#Sending email 
	$smtp.Send($msg)
	$att.Dispose()
}

#=============================================================================
# --- Log Messages to journal
#=============================================================================

function LogMessages($LogMessage, $LogErrorMsg) {
	
	$(Get-Date -Format s) + " [INFO]    " + $LogMessage | Out-file $ReptFile -Encoding ASCII -append
	if ($LogErrorMsg -ne $null)
	{
		$(Get-Date -Format s) + " [ERROR]   " + $LogErrorMsg | Out-file $ReptFile -Encoding ASCII -append
	}
}

#=============================================================================
# --- MAIN PROGRAM
#=============================================================================

Write-Host "[INFO]    ======================================"
Write-Host "[INFO]           Refresh Linked VM Script"
Write-Host "[INFO]    ======================================"
Write-Host "[INFO]"

#=============================================================================
# --- Credentials management
#=============================================================================

if ((Test-Path $CredFile) -eq $false) {
	$cred = new-object system.management.automation.pscredential $CredUser,
		(read-host -assecurestring -prompt "Enter administrator password")
      try
      {
         $cred.Password | ConvertFrom-SecureString | Set-Content $CredFile -ErrorAction Stop
      }
      catch
      {
         $Message = "Error saving credentials"
         $ErrorMessage = $_.Exception.Message
         LogMessages $Message $ErrorMessage 
         SendMail
         Exit
      }
}
else {
   $password = get-content $CredFile | convertto-securestring
	$cred = new-object -typename System.Management.Automation.PSCredential -argumentlist $CredUser,$password
}

#=============================================================================
# --- Connect to vCenter server
#=============================================================================

Write-Host "[INFO]    Connecting to vCenter: " $vCenter

try
{
	Connect-VIServer -server $vCenter -credential $cred -ErrorAction Stop -WarningAction SilentlyContinue | Out-Null
	$Message = "Connecting to vCenter: " + $vCenter
	LogMessages $Message
}
Catch
{
	$Message = "Failed connecting to " + $vCenter
	$ErrorMessage = $_.Exception.Message
	LogMessages $Message $ErrorMessage 
	SendMail
	Exit
}

#=============================================================================
# --- Shutdown current clones
#=============================================================================

ForEach ($SourceVM in $ListOfVMs)
{
	try
	{
		$TargetVM = $Prefix + $SourceVM
      Write-Host "[INFO]    Shutting down" $TargetVM
		$task = Stop-VM -VM $TargetVM -confirm:$false -runasync -ErrorAction Stop
		wait-task -Task $task  | Out-Null
		$Message = "Stopping " + $TargetVM
		LogMessages $Message
	}
	Catch
	{
      Write-Host "[ERROR]   Shutting down " $TargetVM -foregroundcolor Red
	   $Message = "Error powering off "+ $Prefix + $SourceVM 
		$ErrorMessage = $_.Exception.Message
		LogMessages $Message $ErrorMessage
		SendMail
		Exit
	}
}

#=============================================================================
# --- Remove old snapshots
#=============================================================================

ForEach ($SourceVM in $ListOfVMs)
{
	Try
	{
      Write-Host "[INFO]    Deleting Snapshots for" $SourceVM
		$task = Get-VM $SourceVM | Get-Snapshot | Remove-Snapshot -confirm:$false -runasync -ErrorAction Stop
		wait-task -Task $task | Out-Null
		$Message = "Deleting snapshot for " + $SourceVM
		LogMessages $Message
	}
	Catch
	{
      Write-Host "[ERROR]   Failed removing snapshot for" $SourceVM -foregroundcolor Red
      $Message = "Error removing snapshot for " + $SourceVM 
		$ErrorMessage = $_.Exception.Message
		LogMessages $Message $ErrorMessage
		SendMail
		Exit
	}
}

#=============================================================================
# --- Create new snapshots
#=============================================================================

ForEach ($SourceVM in $ListOfVMs)
{
	try
	{
      Write-Host "[INFO]    Creating snapshot for" $SourceVM
		$task = New-Snapshot -VM $SourceVM -Name ($(Get-Date -format s)) -runasync -ErrorAction Stop
		wait-task -Task $task | Out-Null
		$Message = "Creating snapshot for " + $SourceVM
		LogMessages $Message
	}
	Catch
	{
      Write-Host "[ERROR]   Failed creating snapshot for" $SourceVM -foregroundcolor Red
	   $Message = "Error creating snapshot for " + $SourceVM 
		$ErrorMessage = $_.Exception.Message
		LogMessages $Message $ErrorMessage
		SendMail
		Exit
	}
}

#=============================================================================
# --- Power up refreshed environment
#=============================================================================

ForEach ($SourceVM in $ListOfVMs)
{
	try
	{
		$TargetVM = $Prefix + $SourceVM
      Write-Host "[INFO]    Powering up " $TargetVM
		Start-VM -VM $TargetVM -confirm:$false -runasync -ErrorAction Stop | Out-Null
		$Message = "Restarting " + $TargetVM
		LogMessages $Message
	}
	catch
	{
      Write-Host "[ERROR]   Failure starting " $SourceVM -foregroundcolor Red
	   $Message = "Error starting " + $SourceVM 
		$ErrorMessage = $_.Exception.Message
		LogMessages $Message $ErrorMessage
		SendMail
		Exit
	}
}

#=============================================================================
# --- Close session
#=============================================================================

Disconnect-VIServer -server $vCenter -confirm:$false -force
Write-Host "[INFO]    Disconnecting from vCenter"
$Message = "Disconnecting from vCenter"
LogMessages $Message
SendMail
Write-Host

PowerCLI: Linked VMs

March 31, 2015 – 9:58 pm

My current employer has a pre-production environment that is being refreshed nightly from production using a PowerCLI script. I started looking into it, to better understand the inner workings and to see if I could improve things. The objective is to have a pre-production environment that is an identical copy of the production, that can be deployed and refreshed quickly. A full clone could be an option but doesn’t meet some of the requirements. I call these Linked VMs, as they are not Linked Clones.

Here’s how it works.

A snapshot is taken of the production servers, thereby “freezing” the base VMDK disk. A new VM is created for the pre-production and it uses the “frozen” base disk of the production server as it’s own disk, in non-persistent mode. The VM is of course connected to an isolated port group. This basically allows the dev/infra team to have an identical copy of the production servers at any time (just run the refresh script).

While this works very well, I have concerns about using production disks for this purpose. I usually prefer to leave production alone and have either full clones or fully provisioned pre-prod environment. But it’s been running safely for a few years this way so far…

The initial pre-production linked VMs were created manually and I wanted to automate the creation of these pre-prod shells. Here’s a summary of what this script does

  • Removes all snapshots on the source VM
  • Clones the source VM without the virtual hard drives
  • Link the source VM’s hard drives to the new clone in non-persistent mode
  • Change the network interface port group to an isolated one
  • Create a snapshot on the source VM (this shields the production VM from the “sharing” of it’s disks)

And here’s the source:

#=============================================================================
# Script       : Linked VM Creation
# Author       : Marc Bouchard
# Credits      : Luc Dekens, Rob Girard
# Version      : 1.0
# Revision     : 03/31/2015
# Purpose      : Creates a non-persistent copy of a production VM, for testing
#                and pre-production purposes.
#
# Parameters   : Source VM
#
# Command Line: Create-Preprod.ps1 -SourceVM "SERVERA"
#=============================================================================

#=============================================================================
# Command line parameters
#=============================================================================

param (
	[Parameter(Mandatory=$true)]
	[String]$SourceVM = $(throw "Name of source VM required")
)

#=============================================================================
# Screen setup
#=============================================================================

# set regular console colours
[console]::backgroundcolor = "black"
[console]::foregroundcolor = "white"

# clear screen
clear-host

#=============================================================================
# --- Load Powershell Snap-ins
#=============================================================================

if (!(get-pssnapin -name VMware.VimAutomation.Core -erroraction 'SilentlyContinue')) {
	Write-Host "[INFO] Adding PowerCLI Snapin"
	add-pssnapin VMware.VimAutomation.Core -ErrorAction 'SilentlyContinue'
	if (!(get-pssnapin -name VMware.VimAutomation.Core -erroraction 'SilentlyContinue')) {
		Write-Host "[ERROR] PowerCLI snapin NOT added" -ForegroundColor Red
	} Else {
		Write-Host "[INFO] PowerCLI snapin added"
	}
}

#=============================================================================
# CUSTOMIZE THIS SECTION ONLY FOR YOUR ENVIRONMENT
#=============================================================================

$ScriptLocation="C:\Scripts\PreProd\"

$DateOnly = Get-Date -f yyyy-MM-dd

$CredUser = "ServiceAccountUserID"
$CredFile = $ScriptLocation + "Create-PreProd.crd"
$vCenter = "vCenterServerName"
$Prefix = "PRE_"
$IsolatedPortGroup = "IsolatedPortGroupName"

#=============================================================================
# --- Function: New-LinkedVM
#=============================================================================

function New-LinkedVM {

Param
	(
		[parameter(Mandatory=$true)]
 		[ValidateNotNullOrEmpty()]
 		[PSObject]$LinkSource,

 		[parameter(Mandatory=$true)]
 		[ValidateNotNullOrEmpty()]
 		[String]$LinkTarget,

		[parameter(Mandatory=$false)]
 		[ValidateNotNullOrEmpty()]
 		[String]$LinkPortGroup,

		[parameter(Mandatory=$false)]
 		[ValidateNotNullOrEmpty()]
 		[String]$LinkFolder
	)

#=============================================================================

try {
	if ($LinkSource.GetType().Name -eq "string"){
		try {
			$LinkSource = Get-VM $LinkSource -ErrorAction Stop
		}
		catch [Exception]{
			Write-Host "[WARNING] Source VM does not exist ($LinkSource)" -ForegroundColor Yellow
			Return
		}
	}
	elseif ($LinkSource -isnot [VMware.VimAutomation.ViCore.Impl.V1.Inventory.VirtualMachineImpl]){
	 	Write-Host "[WARNING] Invalid object" -ForegroundColor Yellow
 		Return
 	}

	$Exists = Get-VM $LinkTarget -ErrorAction SilentlyContinue
	if ($Exists){
		Write-Host "[WARNING] Target VM already exists ($LinkTarget)" -ForegroundColor Yellow
		Return
	}

	#=============================================================================
	# --- Remove old snapshots
	#=============================================================================
	
	Write-Host "[INFO]    Removing snapshots from source VM: " $SourceVM
	$Snapshot = Get-Snapshot -VM $LinkSource
	
	if ($Snapshot) {
		$Task = Get-VM $LinkSource | Get-Snapshot | Remove-Snapshot -confirm:$false -runasync
		wait-task -Task $Task
	}

	#=============================================================================
	# --- Get source VM information
	#=============================================================================

	if ($PSBoundParameters.ContainsKey('VMFolder')){
		try {
			$Folder = Get-Folder $LinkFolder -Type VM -ErrorAction Stop
 			$CloneFolder = $Folder.ExtensionData.MoRef
		}
		catch [Exception] {
			Write-Host "[WARNING] VM Folder $VMFolder does not exist, using existing folder instead" -ForegroundColor Yellow
 			$CloneFolder = $LinkSource.ExtensionData.Parent
 		}
	}
	else {
		$CloneFolder = $LinkSource.ExtensionData.Parent
	}
		
	#=============================================================================
	# --- Create clone specifications and exclude source hard disks
	#=============================================================================
	
	$CloneSpec = New-Object -TypeName VMware.Vim.VirtualMachineCloneSpec
	$CloneSpec.Location = New-Object -TypeName VMware.Vim.VirtualMachineRelocateSpec
	$CloneSpec.Location.Datastore = $LinkSource.ExtensionData.Datastore[0]
	$CloneSpec.Location.Host = $LinkSource.ExtensionData.Runtime.Host
	$CloneSpec.Config = New-Object -TypeName VMware.Vim.VirtualMachineConfigSpec
 
	Get-HardDisk -VM $LinkSource | %{
  		$DeviceChange = New-Object -TypeName VMware.Vim.VirtualDeviceConfigSpec
  		$DeviceChange.Device = $_.ExtensionData
  		$DeviceChange.Operation = [VMware.Vim.VirtualDeviceConfigSpecOperation]::remove
  		$DeviceChange.FileOperation = [VMware.Vim.VirtualDeviceConfigSpecFileOperation]::destroy
  		$CloneSpec.Config.DeviceChange += $DeviceChange
	}
 
	#=============================================================================
	# --- Create clone
	#=============================================================================

	$CloneTaskMoRef=$LinkSource.ExtensionData.CloneVM_Task($CloneFolder,$LinkTarget,$CloneSpec)
	$CloneTask=Get-View $CloneTaskMoRef
	while("running","queued" -contains $CloneTask.Info.State){
  		sleep 1
  		$CloneTask.UpdateViewData("Info.State")
	}

	#=============================================================================
	# --- Add SourceVM hard disks in non-persistent mode
	#
	# NOTE: The source VM cannot have any snapshots, otherwise the New-HardDisk
	#       addition will fail.
	#=============================================================================

	$AllDisks = Get-HardDisk -VM $LinkSource 
	ForEach ($HD in $AllDisks) {
		Write-Host "[INFO]    Reconfiguring HD: " $HD
		New-HardDisk -VM $LinkTarget -Persistence IndependentNonPersistent -DiskPath $HD.Filename | Out-Null
		}

	#=============================================================================
	# --- Change network interface port groups to isolated network
	#=============================================================================

	$AllNICs = Get-NetworkAdapter -VM $LinkTarget
	
	ForEach ($NIC in $AllNICs) {
		Write-Host "[INFO]    Reconfiguring NIC: " $NIC
		Set-NetworkAdapter -NetworkAdapter $NIC -Portgroup $LinkPortGroup -Confirm:$false | Out-Null
		}

	#=============================================================================
	# --- Create new snapshot for source VM
	#=============================================================================

	Write-Host "[INFO]    Creating snapshot on source VM: " $SourceVM
	$Task = New-Snapshot -VM $LinkSource -Name ($(Get-Date -format s)) -runasync 
	wait-task -Task $Task  | Out-Null

}
catch [Exception]{
	throw "[ERROR]   New-LinkedVM failed to complete..."
	}
}
#=============================================================================
# --- MAIN PROGRAM
#=============================================================================

Write-Host "[INFO]    ======================================"
Write-Host "[INFO]           Create Linked VM Script"
Write-Host "[INFO]    ======================================"
Write-Host "[INFO]"

#=============================================================================
# --- Credentials management
#=============================================================================

if ((Test-Path $CredFile) -eq $false) {
	$cred = new-object system.management.automation.pscredential $CredUser,
		(read-host -assecurestring -prompt "Enter password for privileged account")
      try
      {
         $cred.Password | ConvertFrom-SecureString | Set-Content $CredFile -ErrorAction Stop
      }
      catch
      {
         Write-Host "[ERROR]   Credentials file creation problem" -ForegroundColor Red
         Exit
      }
}
else {
   $password = get-content $CredFile | convertto-securestring
	$cred = new-object -typename System.Management.Automation.PSCredential -argumentlist $CredUser,$password
}

#=============================================================================
# --- Connect to vCenter server
#=============================================================================

try
{
	Write-Host "[INFO]    Connecting to vCenter: " $vCenter
	Connect-VIServer -server $vCenter -credential $cred -ErrorAction Stop -WarningAction SilentlyContinue | Out-Null
}
Catch
{
	Exit
}

#=============================================================================
# --- Create Linked VM
#=============================================================================

$LinkedVM = $Prefix + $SourceVM

New-LinkedVM -LinkSource $SourceVM -LinkTarget $LinkedVM -LinkPortGroup $IsolatedPortGroup

#=============================================================================
# --- Close session
#=============================================================================

Disconnect-VIServer -server $vCenter -confirm:$false -force
Write-Host "[INFO]    Disconnecting from vCenter"
Write-Host

Lab update

November 2, 2014 – 6:59 pm

I got rid of a rack full of equipment (2 ESXi rackmount servers, switches, routers, ASA Firewall, etc…) and replaced everything with a single ESXi server that will run nested environments (for DCA). I read through a few blogs while researching this and from what I can tell, Supermicro motherboards are a safe bet.

Here’s my build:

  • Supermicro X9SRH-7TF motherboard
  • Intel Xeon E5-2609 v2 CPU
  • Noctua NH-U9DX I4 heat sink
  • Kingston KVR16R11D4/16HA 16GB DDR3-1600MHZ ECC x 4 (64GB)
  • Fractal Design Define R4 ATX Mid Tower Window Case Titanium
  • Corsair RM550 Silence ATX
  • Kingston 8GB USB3.0 DataTraveler for the ESXi installation 🙂

I recycled 4 x 1TB SATA disks I had lying around for local storage. Found a good use for it too! (HP VSA). I also already had a Synology box for NFS/iSCSI storage, so I have many options available.

I also added an HP V1910-24G to have hardware based Layer 3 routing. I wish I could afford a gigabit 3750, but this serves the purpose for now.

Once this was all setup, and vSphere/vCenter installed, I started looking for free appliances I could setup to use in my new homelab. First thing I found was Veeam NFR license for VCPs (there are some available for Spiceworks members also). I installed it and during the setup, I saw it supported HP StoreVirtual, so I looked into it (never heard of it before). I found a free 1TB NFR license. I deployed it on my 4 internal 1TB disks (250GB per disk, striped). No RAID5 unfortunately, but it’s for lab use. Anything that requires more security will be deployed on the Synology.

Some links I found…

HP StoreVirtual: http://www8.hp.com/us/en/products/data-storage/server-vsa.html

Citrix NetScaler VPX Express: https://www.citrix.com/downloads/netscaler-adc/virtual-appliances/netscaler-vpx-express.html

A few more listed here: http://teebeedee.org/2014/05/nfr-licenses-vmware-professionals/

So… Off to installing a bunch of stuff and experimenting with my new lab! Then I’ll go back to prepping for the VCAP-DCA…

 

VCAP5-DCA update…

September 6, 2014 – 5:45 pm

Well, I was planning on taking the exam in september but life events prevented me from reaching that goal. I had to undergo surgery twice for a detached retina in my right eye, with bedrest and impaired vision, I haven’t been much in a study mood. I also recently changed job, leaving consulting for a full time position as an infrastructure architect. Been busy!

I did manage to read through the VMware Press book: Networking for VMware Administrators, which I found interesting and provided good extra knowledge to help with the DCA exam.

I was planning on taking the 5.0 exam of the DCA but since I am postponing it until at least december, I will tackle the 5.5 version of the exam instead.

My biggest concern right now is to upgrade my lab for the next 3 years…

Customized AutoLab for VCAP5-DCA

April 5, 2014 – 7:42 pm

I have been busy for the past few weeks deconstructing and customizing the Autolab (http://www.labguides.com/) to suit my needs. I will start preparing for the VCAP5-DCA exam mid-may and while going through the blueprint, I will build a lab workbook to help myself and others prepare for the exam.

I wanted to start with a clean lab and Autolab was a great option! However it contained too many VMs that weren’t needed for my purpose and I wanted to adapt the naming convention to my personal standards.

While digging into it, I started wondering if I could automate the whole installation from start to finish. Well, I did it! 🙂

The lab contains a Domain Controller, NAS, virtual router, 2 ESXi hosts and vCenter server.

This script requires the lab to be hosted on a vSphere 5.x host with a vCenter instance (to deploy the multi-VMs OVF package).

Check it out and give me your feedback! Would love to hear what you think of it!

You can download it here (4GB): LabInstall

Note: The password everywhere in the environment is “Welcome!”

DeployLab

VCAP5-DCD Update

March 3, 2014 – 5:23 pm

Exam passed! I took the exam this morning and boy was that a beast. Even though I read all of the above, I felt unprepared for the exam as I went through it. Some parts were more technical than I expected (it’s a design cert isn’t it??) and some of the visio diagrams were fairly complex. I finished with 2 mins to spare, thanks to a glitch in the exam software that made me clear/reset a whole diagram to be able to resume it. The proctor told me afterwards that they extended the exam by 5 mins to compensate but I have no idea if that’s the case.

Well, I am very happy to have passed it, not with the score I would have hoped to get but  I managed to get through it. Now, onto the VCAP5-DCA! or the Cisco CCDA, or MCSE Exchange 2013… Nah, VMWare it is for 2014!

VCAP5-DCD Certification

January 19, 2014 – 4:02 pm

I am currently preparing for the VMware design certification for vSphere 5. I thought I would list here the resources I am using and hopefully what will result in a pass 🙂 This is the actual order I have used the materials. My approach to studying any topic is usually the same. Get a good global overview (CBT first usually), then deep dive into the material to get more detailed information. In this case, when I received the OCG, it was so small that I started with that. Took me 3 days to read it… I skipped the exercises for now but will get back to them after the CBT.

  1. VMware VCAP5-DCD Official Study Guide
  2. Pluralsight – Designing Vmware Infrastructure
  3. VMware vSphere 5.1 Clustering Deepdive
  4. Managing and Optimizing VMware vSphere Deployments
  5. VMware vSphere Design 2nd Edition

Still working on this, my exam is booked for the beginning of march 2014. Learning tidbits here and there, which should help me with future designs. Glad I’m doing this!

Sequencing virtual machines startup script

January 17, 2014 – 7:56 pm

Whether it’s for disaster recovery, maintenance or for your home lab, it’s nice to be able to start-up machines in a specific order without having to do it manually. You can do it with vApps, but there are operational drawbacks in doing so, from those pesky warnings when you shutdown/startup an individual VM, to the simple fact that a vApp is also a resource pool, which might not be something you need/want.

So I wrote this basic script to solve this problem. It’s really useful for my home lab, since I shut down everything when I’m done working. This way, I can simply fire up my hosts, run the sequencing script and there you go, back in action.

To ensure that VMs are started when the required services are available, configure your VM Tools service dependency to whatever service MUST be running before the next VM starts.

So here’s the script:

##############################################################################################################
#
# .SCRIPT INFO
#   Name       : SeqStart.ps1
#   Author     : Marc Bouchard
#   Revision   : January 16th, 2014
#
# .DESCRIPTION
#   This script starts VMs in the priority order defined in configuration files.
#   User is prompted for credentials for vCenter/Hosts
#
# .PARAMETERS
#   No parameters required
#
# .INPUT FILES
# 000-Hosts.000         = Contains the list of hosts to poll - can be individual hosts or vCenter server(s)
# 00x-description.00x   = VMs to load in specific order, one per line
#
# .EXAMPLE 1
#   PS> SeqStart
#
# .TO DO LIST
#
##############################################################################################################

# Parameters
Param (
    [Parameter(Mandatory=$false )] [string] $StrCounter = "001"
)

# Initialize
Clear-Host
Set-PowerCLIConfiguration -invalidCertificateAction "ignore" -confirm:$false | Out-Null

##############################################################################################################


# Ask for root credentials for ESXi hosts

while ($vCenterCreds -eq $null) 
{
   Write-Host (get-date -uformat %I:%M:%S) "Getting authentication credentials for hosts" -ForegroundColor Green;
   $vCenterCreds = $host.ui.PromptForCredential("VMware ESXi Login", "Please provide ESXi Hosts credentials", "", "")
}

# Check if file exists
if (Test-Path "*.000")
{
   # Read Host names from text file
   Get-Content "*.000" | Sort | ForEach-Object {Connect-VIServer $_ -Credential $vCenterCreds}|Out-Null
}
else
{
   # Error message
   Write-Host (get-date -uformat %I:%M:%S) "The specified Hosts file does not exist." -ForegroundColor Red;
   Exit
}

# Loop through the numbered tasks lists

$NumCounter = 1
$StrCounter="{0:000}" -f $NumCounter

while (Test-Path "*.$StrCounter" ) 
{
   # Read the file contents
   $VMList = Get-Content "*.$StrCounter"
   
   foreach ($VM in ($VMList))
      {
         # If the VM exists, start it!
         $ItExists = Get-VM -Name $VM
         if ($ItExists) {
            # Start VM and wait for VM Tools to be ready (VM Tools have to be setup for service dependency if required inside the VM)
            Write-Host (get-date -uformat %I:%M:%S) "Starting up $VM" -ForegroundColor Green
            Start-VM -VM $VM | Wait-Tools | Out-Null
         }
         else
         {
            Write-Host (get-date -uformat %I:%M:%S) "$VM does not exist" -ForegroundColor Red
         }
      }
   
   $NumCounter++
   $StrCounter="{0:000}" -f $NumCounter
}
# End of loop

# End of script

SCCM 2012 R2 Installation Script (Part 2)

December 30, 2013 – 2:41 pm

Here are the SQL scripts and the VBS.

EnableMicrosoftUpdate.vbs

Set ServiceManager = CreateObject("Microsoft.Update.ServiceManager") 
ServiceManager.ClientApplicationID = "My App" 
'add the Microsoft Update Service by GUID 
Set NewUpdateService = ServiceManager.AddService2("7971f918-a847-4430-9279-4a52d1efe18d",7,"")

This is the SQL Configuration File

SQL2012-ConfigurationFile.ini

[OPTIONS]

IACCEPTSQLSERVERLICENSETERMS="True"

SAPWD="Welcome!"

ACTION="Install"
ENU="True"
QUIET="False"
QUIETSIMPLE="True"
UpdateEnabled="True"
FEATURES=SQLENGINE,SSMS,ADV_SSMS
UpdateSource="MU"
HELP="False"
INDICATEPROGRESS="False"
X86="False"
INSTALLSHAREDDIR="D:\Program Files\Microsoft SQL Server"
INSTALLSHAREDWOWDIR="D:\Program Files (x86)\Microsoft SQL Server"
INSTANCENAME="MSSQLSERVER"
INSTANCEID="MSSQLSERVER"
SQMREPORTING="False"
ERRORREPORTING="False"
INSTANCEDIR="E:\SQLInst"
AGTSVCACCOUNT="NT AUTHORITY\SYSTEM"
AGTSVCSTARTUPTYPE="Automatic"
COMMFABRICPORT="0"
COMMFABRICNETWORKLEVEL="0"
COMMFABRICENCRYPTION="0"
MATRIXCMBRICKCOMMPORT="0"
SQLSVCSTARTUPTYPE="Automatic"
FILESTREAMLEVEL="0"
ENABLERANU="False"
SQLCOLLATION="SQL_Latin1_General_CP1_CI_AS"
SQLSVCACCOUNT="NT AUTHORITY\SYSTEM"
SQLSYSADMINACCOUNTS="BUILTIN\Administrators"
SECURITYMODE="SQL"
SQLBACKUPDIR="F:\SQLBack"
SQLUSERDBDIR="E:\SQLData"
SQLTEMPDBDIR="E:\SQLTemp"
ADDCURRENTUSERASSQLADMIN="False"
TCPENABLED="1"
NPENABLED="0"
BROWSERSVCSTARTUPTYPE="Disabled"

And last, the SQL script to set the memory sizes (didn’t find this info in the unattended script above. Would have been nice but it works with this 🙂 )

SetSQLMemory.sql

USE master

EXEC sp_configure 'show advanced options',1
RECONFIGURE WITH OVERRIDE
GO

USE master
EXEC sp_configure 'min server memory (MB)',8192
EXEC sp_configure 'max server memory (MB)',8192
EXEC sp_configure 'show advanced options',0
RECONFIGURE WITH OVERRIDE
GO

SCCM 2012 R2 Installation Script (Part 1)

December 30, 2013 – 2:31 pm

Well, as promised here is the script to setup all the pre-reqs for SCCM 2012 R2.

To run this, you need to copy the files in the D: drive.

You need:

  • An SQL configuration file (SQL2012-ConfigurationFile.ini)
  • An SQL script to set the memory sizes (SetSQLMemory.sql)
  • A VBS script to enable the Windows Update feature (EnableMicrosoftUpdate.vbs)
  • WUInstall (from this link: http://www.wuinstall.com/index.php/en)
  • This script (Install-SCCM2012R2.ps1)
##############################################################################
#
# Script       : SCCM Server Complete Build
# Author       : Marc Bouchard
# Revision     : 12/27/2013
# Description  : Complete installation after base OS deployment
# Parameters   : None
#
##############################################################################

#=============================================================================
# Active Directory integration

Function CheckDomainMembership
{
   write-host -fore green "Checking if server is member of a domain..."
   $ADMember=$false
   do
   {
      if ((gwmi win32_computersystem).partofdomain -eq $false) 
      {
         write-host -fore green "Joining Domain"
         $domain = Read-Host -Prompt "Enter Domain name to join"
         $user = Read-Host -Prompt "Enter User account with DomainJoin permissions [Administrator]"
         if ($user -eq "") {$user="Administrator"}
         $password = Read-Host -Prompt "Enter password for $user" -AsSecureString 
         $username = "$domain\$user" 
         $credential = New-Object System.Management.Automation.PSCredential($username,$password) 
         Add-Computer -DomainName $domain -Credential $credential

$msgtxt = @"
The server must now restart to join the domain.
`
Please log back in with a domain account that has 
ADMINISTRATIVE RIGHTS in order to update the schema.
`
Simply relaunch this script to continue after reboot.
"@

               $Message = new-object -comobject wscript.shell 
               $intAnswer = $Message.popup($msgtxt, 0,"Confirmation",0) 
               Restart-Computer         
      } 
      else
      {
         $ADMember=$true
         write-host -fore green "Server is joined to domain..."
      }
   }
   until ($ADMember -eq $true)
}

#=============================================================================
Function InstallSQLServer
{
   #==============================================
   # Install SQL Server binaries                  
   #==============================================
   do 
   {
      Write-Host "Insert SQL Server 2012 DVD and press any key to continue ..." -ForegroundColor White
      $x = $host.UI.RawUI.ReadKey("NoEcho,IncludeKeyDown")
   }
   until (Test-Path -path "R:\setup.exe")

   if (!(Test-Path -path "D:\Scripts\SQL2012-ConfigurationFile.ini"))
   {
      Write-Host "SQL Configuration file missing. Aborting..." -ForegroundColor White
      Break
   }
   Write-Host "Installing SQL Server 2012" -ForegroundColor Green
   CMD /C R:\SETUP /ConfigurationFile=D:\Scripts\SQL2012-ConfigurationFile.ini | Out-Null

   #==============================================
   # Configure Memory Limits
   #==============================================

   CMD /C "D:\Program Files\Microsoft SQL Server\110\Tools\Binn\OSQL.EXE" -E -i D:\Scripts\SetSQLMemory.sql | Out-Null

   #==============================================
   # CONFIGURE SQL INTEGRATED AUTHENTICATION MODE 
   #==============================================

   Write-Host "Set SQL Authentication Mode" -ForegroundColor Green

   # Connect to the instance using SMO
   [System.Reflection.Assembly]::LoadWithPartialName('Microsoft.SqlServer.SMO') | out-null
   $s = new-object ('Microsoft.SqlServer.Management.Smo.Server') $env:COMPUTERNAME

   # Get current Authentication Mode
   [string]$nm = $s.Name
   [string]$mode = $s.Settings.LoginMode

   If ($mode -ne "Integrated")
   {
      #Change to Integrated Mode
      $s.Settings.LoginMode = [Microsoft.SqlServer.Management.SMO.ServerLoginMode]::Integrated

      # Make the changes
      $s.Alter()

      NET STOP SQLSERVERAGENT
      NET STOP MSSQLSERVER
      NET START MSSQLSERVER
      NET START SQLSERVERAGENT
   }

   #==============================================
   # Install Latest updates
   #==============================================
   Write-Host "Install latest updates for SQL and OS" -ForegroundColor Green
   CMD /C cscript D:\Scripts\EnableMicrosoftUpdate.vbs /nologo

   #!!! Loop WUinstall until no more updates (errorlevel 2)
   D:\Scripts\WUInstallAMD64.exe /install

}

#=============================================================================
# Main Program
#=============================================================================

CLS
Set-ExecutionPolicy -ExecutionPolicy Unrestricted -Force

#==============================================
# Set Automatic Windows Update
#==============================================
Write-Host "Enabling Automatic Windows Updates" -ForegroundColor Green
CMD /C cscript %windir%\system32\scregedit.wsf /AU 4

#==============================================
# Execute Custom Functions
#==============================================

CheckDomainMembership
InstallSQLServer

#==============================================
# Install required features 
#==============================================

Write-Host "Installing Features" -ForegroundColor Green

Install-WindowsFeature RSAT | Out-Null
Install-WindowsFeature Web-Windows-Auth | Out-Null
Install-WindowsFeature Web-ISAPI-Ext | Out-Null
Install-WindowsFeature Web-Metabase | Out-Null
Install-WindowsFeature Web-WMI | Out-Null
Install-WindowsFeature BITS | Out-Null
Install-WindowsFeature RDC | Out-Null
Install-WindowsFeature NET-Framework-Features | Out-Null
Install-WindowsFeature Web-Asp-Net | Out-Null
Install-WindowsFeature Web-Asp-Net45 | Out-Null
Install-WindowsFeature NET-HTTP-Activation | Out-Null
Install-WindowsFeature NET-Non-HTTP-Activ | Out-Null

#==============================================

Import-Module ActiveDirectory
Get-Module ServerManager | Out-Null

#==============================================
# Fix Firewall Rules
#==============================================

Write-Host "Modify Firewall Rules" -ForegroundColor Green
CMD /C netsh advfirewall firewall add rule name="SQL / SQL Replication" dir=in protocol=tcp localport="1433,4022"  action=Allow

#==============================================
# Install WSUS
#==============================================

Write-Host "Installing WSUS" -ForegroundColor Green
Install-WindowsFeature -Name UpdateServices-Services,UpdateServices-DB -IncludeManagementTools | Out-Null
CMD /C "C:\Program Files\update services\tools\wsusutil.exe" postinstall CONTENT_DIR=E:\Sources\WSUS\WSUS SQL_INSTANCE_NAME=$env:COMPUTERNAME

#==============================================
# Install ADK
#==============================================
Write-Host "Installing ADK" -ForegroundColor Green
$dwnld = "D:\Scripts\Downloads"
if (!(Test-Path -path $dwnld))
 {
 New-Item $dwnld -type directory | Out-Null
 }
$object = New-Object Net.WebClient
$ADKurl = 'http://download.microsoft.com/download/6/A/E/6AEA92B0-A412-4622-983E-5B305D2EBE56/adk/adksetup.exe'
$object.DownloadFile($ADKurl, "$dwnld\adksetup.exe")
Start-Process -FilePath "$dwnld\adksetup.exe" -Wait -ArgumentList "/quiet /features OptionId.DeploymentTools OptionId.WindowsPreinstallationEnvironment OptionId.UserStateMigrationTool" | Out-Null

#==============================================
# Create Container
#==============================================

   Write-Host "Create OU container" -ForegroundColor Green

   $root = (Get-ADRootDSE).defaultNamingContext

   # Check if System Management OU already exists
   $ou = $null

   try
   {
      $ou = Get-ADObject "CN=System Management,CN=System,$root"
   }
   catch
   {
      Write-Host "System Management container does not currently exist."
   }

   if ($ou -eq $null)
   {
      $ou = New-ADObject -Type Container -name "System Management" -Path "CN=System,$root" -Passthru
   }

   # Get the current ACL for the OU
   $acl = get-acl "ad:CN=System Management,CN=System,$root"

   # Get the SCCM Server's SID (the LOCAL machine)
   $computer = get-adcomputer $env:ComputerName
   $sid = [System.Security.Principal.SecurityIdentifier] $computer.SID

   # Create a new access control entry to allow access to the OU
   $adRights = [System.DirectoryServices.ActiveDirectoryRights] "GenericAll"
   $type = [System.Security.AccessControl.AccessControlType] "Allow"
   $inheritanceType = [System.DirectoryServices.ActiveDirectorySecurityInheritance] "All"
   $ace = New-Object System.DirectoryServices.ActiveDirectoryAccessRule $sid,$adRights,$type,$inheritanceType

   # Add the ACE to the ACL, then set the ACL to save the changes
   $acl.AddAccessRule($ace)
   Set-acl -aclobject $acl "ad:CN=System Management,CN=System,$root"

#==============================================
# Extend Schema
# Note: Requires SCCM ISO mounted
#==============================================
   do 
   {
      Write-Host "Insert SCCM 2012 DVD and press any key to continue ..." -ForegroundColor White
      $x = $host.UI.RawUI.ReadKey("NoEcho,IncludeKeyDown")
   }
   until (Test-Path -path "R:\SMSSetup\BIN\x64\ExtADSch.exe")

   Write-Host "Extending Schema" -ForegroundColor Green
   CMD /C R:\SMSSetup\Bin\x64\ExtADSCh.exe

##############################################################################
# INSTALLATION COMPLETED
##############################################################################

Write-Host "DONE!" -ForegroundColor Green