Configure ESXi Scratch Config w/ Powershell/PowerCLI and other advanced settings...

Summary:
Needed to script configure all my 100+ ESXi hosts w/ a scratch location.  Having a permanent scratch location configured is helpful when an error such as a purple screen of death (PSOD) occurs on ESXi.  It is not a requirement, but definitely a best practice.

PreRequisites:
  1. Powershell 2.0 +
  2. PowerCLI 5.1 +
  3. vCenter 4.1 +
  4. Local or Shared Datastore
    • Local is easy if you standardize on naming of a local datastore.  
      • I'll focus on this in my script example.
    • Shared Datastore essentially accomplishes a similar goal of a remote syslog server, you'll want to be sure to separate logs to their own individual directory.
      • Scaling may become an issue unless you focus these shared datastores among clusters rather than all hosts.
Details:


Rather than write a single line to just configure the scratch location, I decided to write something that could host my standard build config info for all advanced settings.  This way I could simply add additional advanced settings to this same script.  This is made simply to also cover anything that a scripted pxe build process may fail to apply.  It's not the most elegant solution, so I more than welcome feedback and tips.
So here it is:
# Stuff to capture errors, yes, it is quite useful.

$Date = Get-Date -Format yyyy.MM.dd-hh.mm.ss
$UserName = (Get-ChildItem env:\username).value

$ErrorLog = @()
# This can also be a network share like \\servername\hiddensharename$
$ErrorLogOutPutPath = "C:\somepath\errors\WhateverNameYouWantHere$($Date)-$($Username).txt" 

# Connecting to my vCenter
Connect-viserver myvCenterServerName -ErrorVariable +ErrorLog

# Here I am gather all the hosts connected to my connected vCenter instance. 
$VMHosts = Get-VMHost -ErrorVariable +ErrorLog


# Initial Option will always have $num, every subsequent defined option should be ($Num +1)
# $NumberofOptions should match the amount of options you plan to update.
# I made this number 20, even though I only have 4
# It just makes it easier to add more later w/o remember to change 
# this number each time I add an option.
$Num = 0
$NumberofOptions = 20

# Any static options that don't have reliance upon data should be defined here.
# These first two options simply define my remote syslog info.
# Key is the important value.  
# To find out what values are available you can use the Get-AdvancedSetting cmdlet
$Options = New-Object VMware.Vim.OptionValue[] ($NumberofOptions)
$Options[$Num]  = New-Object VMWare.Vim.OptionValue
$Options[$Num].Key  = "Syslog.Remote.Hostname"
$Options[$Num].Value  = "192.168.1.5"

$Options[($Num + 1)]  = New-Object VMWare.Vim.OptionValue
$Options[($Num + 1)].Key  = "Syslog.Remote.Port"
$Options[($Num + 1)].Value  = "514"



# Additional Options can be found by search for # Additional Options #

# Starting a Loop statement to run commands against each individual 
# ESXi host aka VMHost in API terms
Foreach ($VMHost in $VMHosts)
{
# I need the datacenter node name here so I can create the directory/directories needed for 
# the scratch and local syslog directories.

$DataCenter = Get-Datacenter -VMHost $vmhost -ErrorVariable +ErrorLog
# In my case, my standard local datastore name is formatted as myESXHostName_local
# This is needed so I can get the uuid of the store the scratch directory.
$DS = Get-Datastore "$($VMHost.NetworkInfo.Hostname)_local" -ErrorVariable +ErrorLog

# Additional Options #
# Here is where I am testing whether the local syslog path exists
# If not, the script will create it.  The path is required for the logging to work.

$PathCheck = Test-Path vmstore:\$DataCenter\"$($VMHost.NetworkInfo.Hostname)_local"\"logfiles\" -ErrorVariable +ErrorLog
If ($Pathcheck -ne $true)
{mkdir vmstore:\$DataCenter\"$($VMHost.NetworkInfo.Hostname)_local"\logfiles -ErrorVariable +ErrorLog}

# Here I define the local syslog datastore path

$Options[($Num + 1)]  = New-Object VMWare.Vim.OptionValue
$Options[($Num + 1)].Key = "Syslog.local.datastorepath"
$Options[($Num + 1)].Value = ("[" + $VMHost.NetworkInfo.Hostname + "_local] /logfiles/" + $VMHost.NetworkInfo.Hostname + ".log")

# Here is where I am testing whether the local scratch path exists
# If not, the script will create it.  The path needs to exist prior to configuration.

$PathCheck = Test-Path vmstore:\$DataCenter\"$($VMHost.NetworkInfo.Hostname)_local"\".locker-$($VMHost.NetworkInfo.Hostname)" -ErrorVariable +ErrorLog
If ($Pathcheck -ne $true)
{mkdir vmstore:\$DataCenter\"$($VMHost.NetworkInfo.Hostname)_local"\".locker-$($VMHost.NetworkInfo.Hostname)" -ErrorVariable +ErrorLog}

# Here I define the scratch directory location using the datastore information
$Options[($Num + 1)] = New-Object VMWare.Vim.OptionValue
$Options[($Num + 1)].Key = "ScratchConfig.ConfiguredScratchLocation"
$Options[($Num + 1)].Value = ("/vmfs/volumes/$($DS.ExtensionData.info.vmfs.uuid)/.locker-$($VMHost.NetworkInfo.Hostname)")

# End Additional Options #

## Here is where we now apply all those defined advanced settings ##
# Begin loop statement to apply each $Option / Advanced Setting, skipping $null entries

Foreach ($Option in ($Options | where {$_ -ne $null}))
{
Write-Host "Checking if $($Option.Key) equals $($Option.Value)"
# Here I'm simply checking to see if the advanced setting is already is configured.
IF ((Get-AdvancedSetting -Name $Option.Key -ErrorAction:SilentlyContinue -ErrorVariable +ErrorLog).Value -ne $Option.Value )
{Write-Host "It doesn't so I'm changing it."
# If it's not configured, then I'm changing it.
Get-AdvancedSetting -Name $Option.Key | Set-AdvancedSetting -Value $Option.Value -ErrorAction:SilentlyContinue -ErrorVariable +ErrorLog
}
# If it is configured, then this simply lets you know that it is.
Else {Write-Host "It does!?  That's weird, moving on."}
}
}
# Outputs an error txt file if any were recorded.

If ($errorLog.Count -gt 0)
{
$ErrorLog | Out-File $LogPath
}

Managing Vmware Infrastructure with Windows Powershell Tfm (Google Affiliate Ad)

No comments: