vRealize Automation certificate script to generate PEM files

I’ve been learning vRealize Automation 7 installs over the last few months.   I developed a script and associated xml config files to create necessary PEM files needed during vRA install.  For an Enterprise install, at least two certificates are required (medium install in my case).  This blog post doesn’t cover vRA install.  Check out Eric Shanks articles here along with VMware docs for install questions.

Regarding certificates, there a few articles that kind of cover creating PEM files.  When completing the install wizard requires the following:

  • PEM file with necessary root / intermediate / certificate in one file,
  • Private Key in another file

The script and associated XML files give flexible to create necessary certificates.  In my case, I’m doing an Medium install, two certificates are required.

  • One PEM file for vRA appliances
  • One PEM file for Iaas / Manager web sites

A medium enterprise install has two vRA appliances and two windows servers hosting Iaas Web and Manager service.   I use SAN (subject alternative names) certificates.

Script:

#Assumptions
#1 - A working internal certificate authority is setup
#2 - A template in your CA setup provides Client and Server authentication
#3 - OpenSSL has been downloaded and placed in C:\OpenSSL
#4 - Account running commands in script has appropriate rights on template in CA
#5 - Each OpenSSL configuration has been configured with appropriate DNS names (script creates this dynamically with input from XML files)

param
(
	[String] $ConfigurationFile = $(throw "Please specify the configuration file for the Content move.`r`nExample:`r`n`tGet-MachineLookup.ps1 -ConfigurationFile `"E:\Directory\ChangeThisPath.xml`"")
)

switch (Test-Path $ConfigurationFile)
	{
	True {Write-Host "Using $ConfigurationFile For Script Variables"
		$P = [xml](Get-Content $ConfigurationFile)
	}
	False {Write-Host "$ConfigurationFile Not Found For Script Variables - Quitting"
		Exit
		}
	}


#Get Properties and assign to local variables from XML file
[string]$certPath = $P.Configuration.Properties.certPath
[string]$subjAltName = $P.Configuration.Properties.subjAltName
[string]$commonName = $P.Configuration.Properties.commonName

[string]#OpenSSL config settings
[string]$openSSLCFGfileName = $P.Configuration.Properties.openSSLCFGfileName
[string]$countryName = $P.Configuration.Properties.countryName
[string]$stateOrProvinceName = $P.Configuration.Properties.stateOrProvinceName
[string]$localityName = $P.Configuration.Properties.localityName
[string]$organizationName = $P.Configuration.Properties.organizationName
[string]$organizationalUnitName = $P.Configuration.Properties.organizationalUnitName

[string]#General variables
[string]$CertificateTemplateName = $P.Configuration.Properties.CertificateTemplateName
[string]$CertificateKeyLength = $P.Configuration.Properties.CertificateKeyLength
[string]$RootCA = $P.Configuration.Properties.RootCA
[string]$OpenSSLPath = $P.Configuration.Properties.OpenSSLPath
[string]$OpenSSLRootDir = $P.Configuration.Properties.OpenSSLRootDir
[string]$CertPassword = $P.Configuration.Properties.CertPassword

function CreateOpenSSLConfig([string]$Path, [string]$subjAltName, [string]$commonName)
{
	Add-Content -path $Path\$openSSLCFGfileName  -value "[ req ]"
	Add-Content -path $Path\$openSSLCFGfileName  -value "default_bits = 2048"
	Add-Content -path $Path\$openSSLCFGfileName  -value "default_keyfile = rui.key"
	Add-Content -path $Path\$openSSLCFGfileName  -value "distinguished_name = req_distinguished_name"
	Add-Content -path $Path\$openSSLCFGfileName  -value "encrypt_key = no"
	Add-Content -path $Path\$openSSLCFGfileName  -value "prompt = no"
	Add-Content -path $Path\$openSSLCFGfileName  -value "string_mask = nombstr"
	Add-Content -path $Path\$openSSLCFGfileName  -value "req_extensions = v3_req"
	Add-Content -path $Path\$openSSLCFGfileName  -value ""
	Add-Content -path $Path\$openSSLCFGfileName  -value "[ v3_req ]"
	Add-Content -path $Path\$openSSLCFGfileName  -value "basicConstraints = CA:FALSE"
	Add-Content -path $Path\$openSSLCFGfileName  -value "keyUsage = digitalSignature,  keyEncipherment,  dataEncipherment, nonRepudiation"
	Add-Content -path $Path\$openSSLCFGfileName  -value "extendedKeyUsage = serverAuth,  clientAuth"
	Add-Content -path $Path\$openSSLCFGfileName  -value "subjectAltName = $($subjAltName)"
	Add-Content -path $Path\$openSSLCFGfileName  -value ""						  
	Add-Content -path $Path\$openSSLCFGfileName  -value "[ req_distinguished_name ]"
	Add-Content -path $Path\$openSSLCFGfileName  -value "countryName = $($countryName)"
	Add-Content -path $Path\$openSSLCFGfileName  -value "stateOrProvinceName = $($stateOrProvinceName)"
	Add-Content -path $Path\$openSSLCFGfileName  -value "localityName = $($localityName)"
	Add-Content -path $Path\$openSSLCFGfileName  -value "0.organizationName = $($organizationName)"
	Add-Content -path $Path\$openSSLCFGfileName  -value "organizationalUnitName = $($organizationalUnitName)"
	Add-Content -path $Path\$openSSLCFGfileName  -value "commonName = $($commonName)"
}

function CreateCertificate([string]$Path)
{
	[string]$certPathCMD = "$($OpenSSLPath) req -new -nodes -out $($Path)\vra-cert.csr -keyout $($Path)\vra-cert.key -config $($Path)\openssl.cfg"
	Add-Content -path "$($Path)\cmds.txt" -value $certPathCMD
	Invoke-Expression -Command $certPathCMD

	#Write RSA key
	[string]$certRSACMD = "$($OpenSSLPath) rsa -in  $($Path)\vra-cert.key -out  $($Path)\vra-cert.key"
	Add-Content -path "$($Path)\cmds.txt" -value $certRSACMD
	Invoke-Expression -Command $certRSACMD

	#Variables for CSR, Certificate and P7B
	$CSRPath = "$($Path)\vra-Cert.csr"
	$CertificatePath = "$($Path)\vra-Cert.cer"
	$p7bPath = "$($Path)\vra-Cert.p7b"

	#Call CA authority and retrieve certificates, p7bfile
	$certCMD = "$Env:SystemRoot\System32\certreq -attrib `"CertificateTemplate:$($CertificateTemplateName)`" -submit -config $RootCA $CSRPath $CertificatePath $p7bPath"
	Add-Content -path "$($Path)\cmds.txt" -value $certCMD
	Invoke-Expression -Command $certCMD 

	#This adds root CA and certificate to PEM files
	$ChainPEMFile = "$($OpenSSLPath) pkcs7 -in $($p7bPath) -print_certs -out $($Path)\chain.pem"
	Add-Content -path "$($Path)\cmds.txt" -value $ChainPEMFile
	Invoke-Expression -Command $ChainPEMFile
}

if(Test-Path -path $OpenSSLRootDir)
{
	#Create Path for config, files 
	New-Item -ItemType Directory -path $certPath -force

	#Create OpenSSL.cfg
	CreateOpenSSLConfig -Path $certPath -subjAltName $subjAltName -commonName $commonName 

	#Create CSR, Request Cert and create PEM file
	CreateCertificate -Path $certPath
}
else
{
	Exit
}

XML Configuration File

<?xml version="1.0" encoding="UTF-8"?>
<!--
#Assumptions
#1 - A working internal certificate authority is setup
#2 - A template is setup to provide Client and Server authentication
#3 - OpenSSL has been downloaded and placed in C:\OpenSSL
#4 - The account running commands below have appropriate rights on the template
#5 - Each OpenSSL configuration has been configured with appropriate DNS names
-->
<Configuration>
	<Properties>
		<certPath>C:\OpenSSL\Certs\vRACerts\vRAva</certPath>
		<subjAltName>DNS: vravaprod, DNS: vravaprod.ss.local, DNS: vra1.ss.local, IP:192.168.1.100, DNS: vra2.ss.local, IP:192.168.1.101, DNS: vra1, DNS:vra2</subjAltName>
		<commonName>vravaprod.ss.local</commonName>
		
		<!--#OpenSSL config settings-->
		<openSSLCFGfileName>openssl.cfg</openSSLCFGfileName>
		<countryName>US</countryName>
		<stateOrProvinceName>MI</stateOrProvinceName>
		<localityName>CityName</localityName>
		<organizationName>Pondering</organizationName>
		<organizationalUnitName>Geek</organizationalUnitName>
		
		<!--#General variables-->
		<CertificateTemplateName>Copy of Web Server</CertificateTemplateName>
		<CertificateKeyLength>2048</CertificateKeyLength>
		<RootCA>dc1.ss.local\ss-DC1-CA</RootCA>
		<OpenSSLPath>C:\OpenSSL\bin\openssl</OpenSSLPath>
		<OpenSSLRootDir>C:\OpenSSL</OpenSSLRootDir>
		<CertPassword>1234</CertPassword>
	</Properties>
</Configuration>
 

 

PowerCLI, Powershell to disconnect CD-Rom

I have a script that runs weekly to disconnect CD-Rom.  There is a function to control if the CD-Rom is disconnected.   We have various NFS shares and one particular NFS mount point hosts ISOs used for specific tasks.   We want to have these disconnected and leave others attached, that is the reason for the extra check.

The script also accepts an XML file to hold parameters.

# Reference for error when disconnecting
# http://kb.vmware.com/selfservice/microsites/search.do?language=en_US&cmd=displayKC&externalId=2092716

param
(
[String] $ConfigurationFile = $(throw “Please specify the configuration file for the Content move.`r`nExample:`r`n`tGet-MachineLookup.ps1 -ConfigurationFile `”E:\Directory\ChangeThisPath.xml`””)
)

switch (Test-Path $ConfigurationFile)
{
True {Write-Host “Using $ConfigurationFile For Script Variables”
$Properties = [xml](Get-Content $ConfigurationFile)
}
False {Write-Host “$ConfigurationFile Not Found For Script Variables – Quitting”
Exit
}
}

#Get Properties and assign to local variables
$vCenterServer=$Properties.Configuration.Properties.vCenterServer
$smtpServer = $Properties.Configuration.Properties.smtpServer
$MailFrom = $Properties.Configuration.Properties.MailFrom
$MailTo1 = $Properties.Configuration.Properties.MailTo1
$MailTo2 = $Properties.Configuration.Properties.MailTo2
$MailCC = $Properties.Configuration.Properties.MailCC
$Datacenter = $Properties.Configuration.Properties.Datacenter
$DisconnectFlag = $Properties.Configuration.Properties.DisconnectFlag
$Output=$Properties.Configuration.Properties.Output
$OutputErrors=$Properties.Configuration.Properties.OutputErrors

#Assuming you are running PowerCLI 6.x
Import-Module VMware.VimAutomation.Vds

function Log([string]$path, [string]$value)
{
Add-Content -Path “$($Path)$($LogDate).txt” -Value $value
}

#Determines if function will disconnect CD-Rom

function DisconnectCDrom ([string] $isoPathValue)
{
switch -wildcard ($isoPathValue)
{
“*Generic-ISO-Folder-Location*” {return $true}
default {return $false}
}
}

#This could probably use some refining 🙂
function VMMail ($MailTo, $VMList)
{
$msg = new-object Net.Mail.MailMessage
$smtp = new-object Net.Mail.SmtpClient($smtpServer)
$msg.From = $MailFrom
$msg.To.Add($MailTo1)
$msg.To.Add($MailTo2)
$msg.CC.Add($MailCC)
$msg.Subject = “CDRoms disconnected for $($Datacenter)”
$MailText = “This is a summary of VM’s with CD Rom Disconnected for $($Datacenter) `r`n $($VMList) ”
$msg.Body = $MailText
$smtp.Send($msg)
}

$StartDate = Get-Date
$LogDate = “$($StartDate.Month)-$($StartDate.Day)-$($StartDate.Year)-$($StartDate.Hour)-$($StartDate.Minute)-$($vCenterServer)”
Log -Path $Output -Value “Starting process as $($Cred.Username) connecting to $($vCenterServer) at $($StartDate)”

#Notice the -force is used, when running in task scheduler, set user # creds with the account
#With perms assigned in vCenter

Connect-VIServer -server $vCenterServer -force
$VMList = Get-Datacenter -Name $Datacenter | Get-VM

$ListOfVMs = @()

foreach($vm in $VMList)
{
if($vm.PowerState -eq “PoweredOn”)
{
Write-Host “Processing $($VM.Name)”
$CDStatus = Get-CDDrive -VM $VM.Name
if($CDStatus.IsoPath -ne $null)
{
$value1 = “$($VM.Name)!$($CDStatus.IsoPath)!$($CDStatus.HostDevice)!$($CDStatus.RemoteDevice)”
Write-Host $value1
$DisconnectCDRom = DisconnectCDrom -isoPathValue $CDStatus.IsoPath
if($DisconnectCDRom -eq $true)
{
Write-Host “Disconnect CDRom for $($VM.Name)”
if($DisconnectFlag -eq 1)
{
$VM | Get-CDDrive | Set-CDDrive -NoMedia -Confirm:$false
$DisconnectDateTime = Get-Date
$ListOfVMs += “$($VM.Name) : $($DisconnectDateTime)`r`n”
Log -Path $Output -Value $value1
}
else
{
Log -Path $Output -Value “$($VM.Name) – Disconnect Flag set to false”
}
}
}
else
{
$value1 = “$($VM.Name)!no CDRom attached!!”
Write-Host $value1
Log -Path $Output -Value $value1
}
}
else
{
$value1 = “$($VM.Name)!powered off!!”
Write-Host $value1
Log -Path $Output -Value “$($value1)”
}
}

#Send email to appropriate people
if($ListOfVMs -ne $null)
{
VMMail -MailTo $MailFrom -VMList $ListOfVMs
}

#End Logging date
$EndDate = Get-Date
Log -Path $Output -Value “Ending process as $($Cred.Username) connecting to $($vCenterServer) at $($EndDate)”
Disconnect-VIServer -Server $vCenterServer -confirm:$false

Configuration file – ConfigurationFile.xml

<?xml version=”1.0″ encoding=”UTF-8″?>
<Configuration>
<Properties>
<vCenterServer>vCenterName</vCenterServer>
<smtpServer>mail.example.com</smtpServer>
<MailFrom>adminAccount@example.com</MailFrom>
<MailTo1>user1@example.com</MailTo1>
<MailTo2>group1@example.com</MailTo2>
<MailCC>user2@example.com</MailCC>
<Datacenter>DataCenterName</Datacenter>
<DisconnectFlag>1</DisconnectFlag>
<Output>C:\Scripts\Disconnect-CDRom\Logs\Disconnect-CDRom</Output>
<OutputErrors>C:\Scripts\Disconnect-CDRom\Logs\Disconnect-CDRomErrors</OutputErrors>
</Properties>
</Configuration>

Automate backing up DVS using Powercli and POwerShell

We started using Distributed Virtual Switches (DVS) with VMware (vSphere).  The switches configure (and port groups) can be exported and used as a backup.   The following script is a modified version of one posted in the links listed below. 

PS: As of this release, there is a bug where the path to the modules regarding PowerCLI isn’t automatically added to the Machine Level environment variable PSModulePath.  I manually added mine to the utility machine running the script

Adjustments introduces in the script:

  • Keeps around 30 days of configuration files on disk for backups
  • Process multiple vCenters in single script
  • Externalizes configuration into an XML file

Assumptions

  • Using PowerCLI 6.0 and modules (in reference links)
  • Using vCenter 5.5 and above
  • Using Windows Task Scheduler (in the connect-viserver I use –force to inherit the permissions of the Windows Task Scheduler ID)
  • The Windows Task scheduler account has “Logon As Batch Job” permissions.
  • E:\Scripts\BackupDVS folder is present.

#Reference links (original script came from bolded link)

#http://vcdx56.com/2013/10/29/backup-vsphere-vnetwork-distributed-switches-using-powercli/
#https://communities.vmware.com/thread/512171
#https://technet.microsoft.com/en-us/library/Ee692801.aspx
#https://blogs.vmware.com/PowerCLI/2015/03/powercli-6-0-introducing-powercli-modules.html
#https://blogs.vmware.com/PowerCLI/2015/03/powercli-6-0-r1-now-generally-available.html

PowerShell script (backupdvs.ps1)

param
(
    [String] $ConfigurationFile = $(throw “Please specify the configuration file for the Content move.`r`nExample:`r`n`tGet-MachineLookup.ps1 -ConfigurationFile `”E:\Directory\ChangeThisPath.xml`””)
)

Import-Module VMware.VimAutomation.Vds

switch (Test-Path $ConfigurationFile)
    {
    True {Write-Host “Using $ConfigurationFile For Script Variables”
        $Properties = [xml](Get-Content $ConfigurationFile)
    }
    False {Write-Host “$ConfigurationFile Not Found For Script Variables – Quitting”
        Exit
        }
    }
   
$vCenters=$Properties.Configuration.Properties.vCenterList
$vCenterList = $vCenters.Split(“;”)
$BackupFolder = $Properties.Configuration.Properties.BackupFolder

foreach($vCenter in $vCenterList)
{
    $date=get-date -uformat %d
    $BackupPath=”$($BackupFolder)\$($Date)\$($vCenter)”

    New-item -Type Directory -Path $BackupPath -force   

    connect-viserver $vcenter -force
    $switches=get-vdswitch
    foreach ($switch in $switches)
    {
        #
        # Backup each vNetwork Distributed Switch not including the port groups
        export-vdswitch $switch -Withoutportgroups -Description “Backup of $switch without port groups” -Destination “$($BackupPath)\$switch.without_portgroups.zip” -force
        #
        # Backup each vNetwork Distributed Switch including the port groups
        export-vdswitch $switch -Description “Backup of $switch with port groups” -Destination “$($BackupPath)\$switch.with_portgroups.zip” -force
        #
        # Backup each port group individually
        get-vdswitch $switch | Get-VDPortgroup | foreach { export-vdportgroup -vdportgroup $_ -Description “Backup of port group $($_.name)” -destination “$($BackupPath)\$($_.name).portgroup.zip”  -force}
    }
}

External Configuration file (backupdvs.xml)

<?xml version=”1.0″ encoding=”UTF-8″?>
<Configuration>
    <Properties>       
        <vCenterList>vCenter1;vCenter2</vCenterList>
        <BackupFolder>E:\Scripts\BackupDVS</BackupFolder>
        <Output>Backup-VDSwitch</Output>
        <OutputErrors>Backup-VDSwitchErrors</OutputErrors>
    </Properties>
</Configuration>

Create a linked clone from a parent vm using vmware sdk

I was working on a project to create a 90 day eval machine that used linked clones.  This article isn’t meant to educate on Linked clones although it’s a great concept to have a Parent VM and another disk that contains just changes.  The benefits saves a lot of space in storage.   

Text only version of script
http://iislogs.com/ss/linkedclone.txt

Here is a couple articles to check on linked clones

Here is the script

param(
    [string]$ParentVMName = “UP1”,
    [string]$vCenterHostName = “vCenterSS”,
    [string]$vCenterUserName= “domain\user”,
    [string]$vCenterUserPassword= “UP2”,
    [string]$cloneName = “UP3”,
    [string]$OSType = “Windows”,
    [string]$TimeZone = “035”,
    [string]$DHCPPortGroup = “123456”,
    [string]$Domain = “example.com”,
    [string]$DomainUserName = “user@example.com”,
    [string]$DomainPassword = “UP4”,
    [string]$AdminPassword = “changeme”,
    [string]$FullName = “CompanyA”,
    [string]$OrgName = “Example, Inc”,
    [string]$CustomizeTemplate1 = “$($cloneName)-Temp1”,
    [string]$CustomizeTemplate2 = “$($cloneName)-Temp2”,
    [string]$ServerDescription = “UP5”,
    [string]$PrimaryDNSSuffix = “example.com”,
    #This is a enum VM, PREFIX CUSTOM
    [string]$NamingScheme=”VM”
    )

function LoadSnapin{
  param($PSSnapinName)
  if (!(Get-PSSnapin | where {$_.Name -eq $PSSnapinName})){
    Add-pssnapin -name $PSSnapinName
  }
}
LoadSnapin -PSSnapinName   “VMware.VimAutomation.Core”

#Better to pass in an array and randomly select datastore
function GetDataStore
{
    $value = (Get-Random) %3
    switch ($value)
    {
        0 {    return “datastore1”    }
        1 {    return “datastore2”    }
        2 {    return “datastore3”    }
        default {return “datastore1”}
    }
}   

# Constants for status
$STATUS_VM_NOT_STARTED = “VmNotStarted”
$STATUS_CUSTOMIZATION_NOT_STARTED = “CustomizationNotStarted”
$STATUS_STARTED = “CustomizationStarted”
$STATUS_SUCCEEDED = “CustomizationSucceeded”
$STATUS_FAILED = “CustomizationFailed”
$STATUS_NOT_COMPLETED_LIST = @( $STATUS_CUSTOMIZATION_NOT_STARTED, $STATUS_STARTED )
# constants for event types     
$EVENT_TYPE_CUSTOMIZATION_STARTED = “VMware.Vim.CustomizationStartedEvent”
$EVENT_TYPE_CUSTOMIZATION_SUCCEEDED = “VMware.Vim.CustomizationSucceeded”
$EVENT_TYPE_CUSTOMIZATION_FAILED = “VMware.Vim.CustomizationFailed”
$EVENT_TYPE_VM_START = “VMware.Vim.VmStartingEvent”
# seconds to sleep before next loop iteration
$WAIT_INTERVAL_SECONDS = 15
[int] $timeoutSeconds = 1200

function GetOSCustomizationSpecStatus($vm, $timeoutSeconds)
{
   # the moment in which the script has started
   # the maximum time to wait is measured from this moment
   $startTime = Get-Date
   # we will check for “start vm” events 5 minutes before current moment
   $startTimeEventFilter = $startTime.AddMinutes(-5)
   # initializing list of helper objects
   # each object holds VM, customization status and the last VmStarting event
   $vmDescriptors = New-Object System.Collections.ArrayList

   Write-Host “Start monitoring customization process for vm ‘$vm'”
   $obj = “” | select VM,CustomizationStatus,StartVMEvent
   $obj.VM = $vm
   # getting all events for the $vm,
   #  filter them by type,
   #  sort them by CreatedTime,
   #  get the last one
   $obj.StartVMEvent = Get-VIEvent -Entity $vm -Start $startTimeEventFilter | where { $_ -is $EVENT_TYPE_VM_START } | Sort CreatedTime | Select -Last 1
    if (-not $obj.StartVMEvent)
    {
        $obj.CustomizationStatus = $STATUS_VM_NOT_STARTED
    } else
    {
        $obj.CustomizationStatus = $STATUS_CUSTOMIZATION_NOT_STARTED
    }
    [void]($vmDescriptors.Add($obj))
   # declaring script block which will evaluate whether
   # to continue waiting for customization status update
   $shouldContinue = {
      # is there more virtual machines to wait for customization status update
      # we should wait for VMs with status $STATUS_STARTED or $STATUS_CUSTOMIZATION_NOT_STARTED
      $notCompletedVms = $vmDescriptors | where { $STATUS_NOT_COMPLETED_LIST -contains $_.CustomizationStatus }
      # evaluating the time that has elapsed since the script is running
      $currentTime = Get-Date
      $timeElapsed = $currentTime – $startTime
      $timoutNotElapsed = ($timeElapsed.TotalSeconds -lt $timeoutSeconds)
      # returns $true if there are more virtual machines to monitor
      # and the timeout is not elapsed
      return ( ($notCompletedVms -ne $null) -and ($timoutNotElapsed) )
   }
   while (& $shouldContinue)
   {
      foreach ($vmItem in $vmDescriptors)
      {
         $vmName = $vmItem.VM.Name
         switch ($vmItem.CustomizationStatus)
         {
            $STATUS_CUSTOMIZATION_NOT_STARTED
            {
               # we should check for customization started event
               $vmEvents = Get-VIEvent -Entity $vmItem.VM -Start $vmItem.StartVMEvent.CreatedTime
               $startEvent = $vmEvents | where { $_ -is $EVENT_TYPE_CUSTOMIZATION_STARTED }
               if ($startEvent) {
                  $vmItem.CustomizationStatus = $STATUS_STARTED
                  Write-Host “Customization for VM ‘$($vm)’ has started”
               }
               break;
            }
            $STATUS_STARTED
            {
               # we should check for customization succeeded or failed event
               $vmEvents = Get-VIEvent -Entity $vmItem.VM -Start $vmItem.StartVMEvent.CreatedTime
               $succeedEvent = $vmEvents | where { $_ -is $EVENT_TYPE_CUSTOMIZATION_SUCCEEDED }
               $failedEvent = $vmEvents | where { $_ -is $EVENT_TYPE_CUSTOMIZATION_FAILED }
               if ($succeedEvent)
               {
                  $vmItem.CustomizationStatus = $STATUS_SUCCEEDED
                  Write-Host “Customization for VM ‘$($vm)’ has successfully completed”
               }
               if ($failedEvent)
               {
                  $vmItem.CustomizationStatus = $STATUS_FAILED
                  Write-Host “Customization for VM ‘$($vm)’ has failed”
               }
               break;
            }
            default
            {
               # in all other cases there is nothing to do
               #    $STATUS_VM_NOT_STARTED -> if VM is not started, there’s no point to look for customization events
               #    $STATUS_SUCCEEDED -> customization is already succeeded
               #    $STATUS_FAILED -> customization
               break;
            }
         } # end of switch
      } # end of the foreach loop
      Write-Host “Sleeping for $WAIT_INTERVAL_SECONDS seconds”
      Sleep $WAIT_INTERVAL_SECONDS
   } # end of while loop
   # preparing result, without the helper column StartVMEvent
   $result = $vmDescriptors | select VM,CustomizationStatus
   Write-Host “I’m here $result[1]”

   if($result[1] -ne “CustomizationSucceeded”)
   {
    write-host “Waiting for VM Tools to Start”
<#
    do {

    $toolsStatus = (Get-VM $cloneName).extensiondata.Guest.ToolsStatus

    write-host $toolsStatus

    start-sleep -s 5

    } until ( $toolsStatus -eq ‘toolsOk’ )
#>
    Write-Host “Tools are running”

    $InvokeAdminPassword = ConvertTo-SecureString -String $AdminPassword -asplaintext -force
    $cmdAddToDomain = “netdom join /d:$($Domain) $cloneName /ud:$($DomainUserName) /pd:$($DomainPassword) /reboot:30”
    If ($ParentVMName -match ‘DEV1’ )
        {
            $varAddToDomain = Invoke-VMScript -VM $cloneName -ScriptText $cmdAddToDomain -GuestUser “DevAdmin1” -GuestPassword $InvokeAdminPassword
        }
    Else
        {
            $varAddToDomain = Invoke-VMScript -VM $cloneName -ScriptText $cmdAddToDomain -GuestUser “Admin1” -GuestPassword $InvokeAdminPassword
        }
    $varAddToDomain = Invoke-VMScript -VM $cloneName -ScriptText $cmdAddToDomain -GuestUser “Admin1” -GuestPassword $InvokeAdminPassword
    Write-Host $varAddToDomain
   }
   return $result
}

function AddToDomain
{
    write-host “Waiting for VM Tools to Start”

<#    do {

    $toolsStatus = (Get-VM $cloneName).extensiondata.Guest.ToolsStatus

    write-host $toolsStatus

    start-sleep -s 5

    } until ( $toolsStatus -eq ‘toolsOk’ )
#>
    Write-Host “Tools are running”

    Write-Host “Add to Domain using Netdom”
    $InvokeAdminPassword = ConvertTo-SecureString -String $AdminPassword -asplaintext -force
    $cmd = “netdom join /d:$($Domain) $cloneName /ud:$($DomainUserName) /pd:$($DomainPassword) /reboot:30”
        If ($ParentVMName -match ‘Dev1’ )
        {
            $netDomOutPut = Invoke-VMScript -VM $cloneName -ScriptText $cmd -GuestUser “DevAdmin1” -GuestPassword $InvokeAdminPassword
        }
        Else
        {
            $netDomOutPut = Invoke-VMScript -VM $cloneName -ScriptText $cmd -GuestUser “Admin1” -GuestPassword $InvokeAdminPassword
        }
    $netDomOutPut = Invoke-VMScript -VM $cloneName -ScriptText $cmd -GuestUser “Admin1” -GuestPassword $InvokeAdminPassword
    $date = Get-Date -Format MM-dd-yy

    #Adding output of netdom for Scorch to consume
    Add-Content -path “F:\Logs\NetDomOutput\$($cloneName)-$($date).txt” -value $netDomOutPut -force
    ipconfig /registerdns
}

Connect-VIServer -Server $vCenterHostName -User $vCenterUserName -Password $vCenterUserPassword

    #Creates the VM
    $sourceVM = Get-VM $ParentVMName | Get-View
    $cloneFolder = $sourceVM.parent
    $cloneSpec = new-object Vmware.Vim.VirtualMachineCloneSpec
    $cloneSpec.Snapshot = $sourceVM.Snapshot.CurrentSnapshot
    $cloneSpec.Location = new-object Vmware.Vim.VirtualMachineRelocateSpec

    #Creates Linked clone
    $cloneSpec.Location.DiskMoveType = [Vmware.Vim.VirtualMachineRelocateDiskMoveOptions]::createNewChildDiskBacking

    #Defines the Datastore, calls a function to get a specific datastore
    $Datastore = GetDataStore
    $cloneSpec.Location.Datastore = (Get-View -ViewType Datastore -Property Name -Filter @{“Name”=$DataStore}).MoRef
    $sourceVM.CloneVM_Task( $cloneFolder, $cloneName, $cloneSpec )

    $Spec1 = New-OSCustomizationSpec -FullName $FullName -OrgName $OrgName -OSType $OSType -ChangeSID -Name $CustomizeTemplate1 -Type NonPersistent -WorkGroup WK -AdminPassword $AdminPassword -TimeZone $TimeZone -Description $ServerDescription -LicenseMode PerSeat -DnsSuffix $PrimaryDNSSuffix
    Start-Sleep -s 4
    $Spec1 = Get-OSCustomizationSpec $CustomizeTemplate1
    Start-Sleep -s 4

    Get-VM $cloneName | Set-VM -OSCustomizationSpec $Spec1 -Confirm:$false -Description $ServerDescription | Start-VM
    $NIC = Get-NetworkAdapter -VM $cloneName
    Set-NetworkAdapter -NetworkAdapter $NIC -Connected $false -StartConnected $false -Confirm:$false
    Start-Sleep -s 4

    GetOSCustomizationSpecStatus -Vm $cloneName -timeoutSeconds 1200

    $a = Get-VM -Name $cloneName
    Stop-VM -VM $a -confirm:0

    Start-Sleep -m 250

    while ($a.PowerState -eq “PoweredOn”)
    {   
        Write-Host “Sleeping for 5 seconds”
        $a = get-vm -Name $cloneName
        start-sleep -s 5
    }

    Get-VM $cloneName  | Start-VM
    Start-Sleep 30
    $NIC = Get-NetworkAdapter -VM $cloneName
    Set-NetworkAdapter -NetworkAdapter $NIC -NetworkName $DHCPPortGroup -Connected $true -StartConnected $true -Confirm:$false
    Start-Sleep 30

    #GetOSCustomizationSpecStatus -Vm $cloneName -timeoutSeconds 1200
    AddToDomain
    Remove-OSCustomizationSpec -OSCustomizationSpec $Spec1 -Confirm:$false

Disconnect-VIServer -Server $vCenterHostName -Confirm:$false | out-null

process files with .TMP file extensions

We received a question how to process with .TMP file extensions.  IISLogs has a feature called Per Directory that was introduced in 2.0.  The feature allows an administrator to have granular control on a per directory basis.  We added the .TMP extension as an option in the Per Directory feature, please review the Per Directory article for complete options.  If you have any questions, feel free to contact our support alias @ info@iislogs.com

Open IISLogsGUI, Select Per Directory option

Fill in the Directory Name and other attributes you need.

Select TMP

Capture

When you save this, in the install folder the data is stored in a file called IISLogsPerDirectory.xml

<?xml version=”1.0″ standalone=”yes”?>
<NewDataSet>
  <Table1>
    <DirectoryName>c:\inetpub\temp\test\</DirectoryName>
    <ZipFile>false</ZipFile>
    <ZipRetentionPeriod>0</ZipRetentionPeriod>
    <DeleteOriginalFile>false</DeleteOriginalFile>
    <DeleteFile>true</DeleteFile>
    <DeleteRetentionPeriod>48</DeleteRetentionPeriod>
    <Recursive>false</Recursive>
    <ProcessRootFolderRecursive>false</ProcessRootFolderRecursive>
    <ZipFilePath>local</ZipFilePath>
    <IncludeComputerName>false</IncludeComputerName>
    <ProcessUnknownExtensions>false</ProcessUnknownExtensions>
    <ProcessTXT>false</ProcessTXT>
    <ProcessBAK>false</ProcessBAK>
    <ProcessDAT>false</ProcessDAT>
    <ProcessXML>false</ProcessXML>
    <NamingConvention>Default</NamingConvention>
    <Delimiter>!</Delimiter>
    <ProcessEXE>false</ProcessEXE>
    <ProcessMSP>false</ProcessMSP>
    <ProcessDLL>false</ProcessDLL>
    <ProcessINI>false</ProcessINI>
    <ProcessCFG>false</ProcessCFG>
    <ProcessTMP>true</ProcessTMP>
    <LogsDWM>1</LogsDWM>
    <PreserveDirPath>true</PreserveDirPath>
  </Table1>
</NewDataSet>

Cheers,

Steve Schofield
Microsoft MVP – IIS

The application has failed to start because its side-by-side configuration is incorrect error in IISLogs

We have an occasional support question a person receives an error when trying to execute IISLogs.

Problem:
The problem is a space “&nbsp;” or “&#x0;!” stored within the configuration item normally causes the issue.

<add key=”MonitoredEntireDirectories” value=”&nbsp;” />

or

<add key=”MonitoredEntireDirectories” value=”&#x0;!” />

Solution:

Remove the “&nbsp;” or “&#x0!” value using a text editor such as Notepad.

side-by-side-error3

Here are screenshots to showing the error and where to look.

side-by-side-error1

side-by-side-error2

Ping Sweep test using PowerCLI and vCenter

I had a reason to do a ping sweep across a set of machines with vCenter using PowerCLI.  The Test-Connection cmdlet is available on Win 8 / Win 2012 R2.   Enjoy.

# Assuming you’ve loaded the PowerCLI snapin located @
# http://blogs.vmware.com/PowerCLI/2014/09/new-release-vmware-vsphere-powercli-5-8-release-1.html
# Loading PowerCLI Snapins
function LoadSnapin{
param($PSSnapinName)
if (!(Get-PSSnapin | where {$_.Name   -eq $PSSnapinName})){
Add-pssnapin -name $PSSnapinName
}
}
LoadSnapin -PSSnapinName   “VMware.VimAutomation.Core”

#An account is needed
$cred = Get-Credential “domain\user”

#Connect to vCenter
Connect-VIServer -server “vCenterServer” -credential $cred
$a = Get-Cluster “Server-Cluster-Name” | get-vm
$results = @()
foreach($computer in $a)
{
Write-Host $Computer.Name
$results += Test-Connection -ComputerName $Computer.Name -count 2
}

$results | Select Address,IPV4Address,ReplySize,ResponseTime | Export-Csv results.csv
Here is a sample output

#TYPE Selected.System.Management.ManagementObject
“Address”,”IPV4Address”,”ReplySize”,”ResponseTime”
“Server1″,”192.168.10.50″,”32″,”0”
“Server1″,”192.168.10.50″,”32″,”0”
“Server2″,”192.168.11.70″,”32″,”0”
“Server2″,”192.168.11.70″,”32″,”0”

VMware Snapshot reminder script

Here is a modified script for sending notifications to owners of Snapshots.    I found a couple of examples I hacked together.  I added a function to help Sanitize based on three different attributes. I consolidated them into a single function that determines to send a notification or not.  We have some static machines used for packaging software that maintain a single snapshot.  I included the two links of the original articles that have notification routines.

I wish the user who created the snapshot would be recorded on each snapshot as the owner.  A person has to retrieve the owner from the tasks / events, which is a bit of extra work.  I’ve been running this script for a couple months and been helpful. 

Enjoy

Steve Schofield
Microsoft MVP – IIS

 

# – SnapReminder V1.0 By Virtu-Al – http://virtu-al.net
#
# Please use the below variables to define your settings before use
# function GetSnapList uses http://virtualcurtis.wordpress.com/2011/10/02/find-virtual-machine-snapshots-with-powercli/
param
(
    [string]$vCenterServer = $(throw “Please specify the vCenter server you want to query for snapshots.”),
    [string]$smtpServer = “mail.example.com”,
    [string]$MailFrom = “steve@example.com”,
    [string]$CredentialID = “domain\user”,
    [int]$AgeOfSnap = 14,
    [string]$Output = “Output”,
    [string]$OutputErrors = “OutputErrors”,
    [string]$OutputEmailAudit = “OutputEmailAudit”   
)

function Log([string]$path, [string]$value)
{
    Add-Content -Path “$($Path)$($LogDate).txt” -Value $value
}

# Loading Snapins
function LoadSnapin{
  param($PSSnapinName)
  if (!(Get-PSSnapin | where {$_.Name   -eq $PSSnapinName})){
    Add-pssnapin -name $PSSnapinName
  }
}
LoadSnapin -PSSnapinName   “VMware.VimAutomation.Core”

function Find-User ($username){
    if ($username -ne $null)
    {
        $usr = (($username.split(“\”))[1])
        $root = [ADSI]””
        $filter = (“(&(objectCategory=user)(samAccountName=$Usr))”)
        $ds = new-object system.DirectoryServices.DirectorySearcher($root,$filter)
        $ds.PageSize = 1000
        $ds.FindOne()
    }
}

function GetSnapList
{

$myVMs = Get-VM
$VMsWithSnaps = @()

foreach ($vm in $myVMs)
{
    $vmView = $vm | Get-View
    if ($vmView.snapshot -ne $null)
    {
         $SnapshotEvents = Get-VIEvent -Entity $vm -type info -MaxSamples 1000 | Where { $_.FullFormattedMessage.contains(“Create virtual machine snapshot”)}
        try
        {
            $user = $SnapshotEvents[0].UserName
            $time = $SnapshotEvents[0].CreatedTime
        }
        catch [System.Exception]
        {
                $user = $SnapshotEvents.UserName
                $time = $SnapshotEvents.CreatedTime
        }
       $VMInfo = “” | Select “VM”,”CreationDate”,”User”
       $VMInfo.”VM” = $vm.Name
       $VMInfo.”CreationDate” = $time
       $VMInfo.”User” = $user
       $VMsWithSnaps += $VMInfo
    }
}

#Causes the array to be doubled
#$VMsWithSnaps | Sort CreationDate

return $VMsWithSnaps
}

function SnapMail ($Mailto, $snapshot)
{
    $msg = new-object Net.Mail.MailMessage
    $smtp = new-object Net.Mail.SmtpClient($smtpServer)
    $msg.From = $MailFrom
    $msg.To.Add($Mailto)
    $msg.Subject = “Snapshot Reminder”
    $MailText = “This is a reminder that you have a snapshot active on $($snapshot.VM) which was taken on $($snapshot.Created)”       
    $msg.Body = $MailText
    $smtp.Send($msg)
    $VMDiskSize = [Math]::Round(($snapshot.SizeGB),2)
    Log -Path $OutputEmailAudit -Value “$($Mailto),$($snapshot.VM),$($snapshot.Created),$($VMDiskSize) GB”   
}

#Sanitize if a notification is sent to owner of Snap
function Sanitize ($SnapInfo,[string]$VMName)
{
    [string]$SnapDescription=$SnapInfo.Description
    [string]$SnapName=$SnapInfo.Name
   
    switch -wildcard ($VMName)
    {
        “*abc*” {return $false}
        “*xyz*” {return $false}
        “*123*” {return $false}
        “*xp*” {return $false}
        #”*templates*” {return $false}
    }   
   
    switch -wildcard ($SnapDescription)
    {
        “*abc*” {return $false}
        “*xyz*” {return $false}
        “*123*” {return $false}
        “*xp*” {return $false}
        #”*templates*” {return $false}
    }   
   
    switch -wildcard ($SnapName)
    {
        “*abc*” {return $false}
        “*xyz*” {return $false}
        “*123*” {return $false}
        “*xp*” {return $false}
        #”*templates*” {return $false}
        default {return $true}
    }
}

[double]$SnapSpaceUsed=0
[int]$SnapCount=0
$StartDate = Get-Date
$LogDate = “$($StartDate.Month)-$($StartDate.Day)-$($StartDate.Year)-$($StartDate.Hour)-$($StartDate.Minute)-$($vCenterServer)”
$cred = Get-Credential $CredentialID
Connect-VIServer -server $vCenterServer -credential $cred
Log -Path $Output -Value “Starting process as $($Cred.Username) connecting to $($vCenterServer) at $($StartDate)”

$vmlist = GetSnapList

#Get a list of machines that have snapshots
#Loop through it
foreach ($snapdata in $vmlist)
{
    Write-Host “Starting VM – $($snapdata.vm)”
    #Start process that shows creation date is l
     if($snapdata.CreationDate -lt ((Get-Date).AddDays(-$AgeOfSnap)))
    {
      #Get a list of snaphosts per vm
      #Loop through the list of VM’s
      $snapPerVM = Get-VM -Name $snapdata.vm | Get-SnapShot
      foreach($snap in $snapPerVM)
      {           
           [Boolean]$ProcessNotification = Sanitize -SnapInfo $snap -VMName $snapdata.vm
               Log -Path $Output -Value “*************************************************”
            Log -Path $Output -Value “Starting VM – $snapdata.vm”
            Log -Path $Output -Value “Process : $($ProcessNotification) : Snap : $($snap)”
          
           if($ProcessNotification -eq $true -and $snapdata.User -ne $null)
           {
                $mailto = ((Find-User $snapdata.User).Properties)
                $mailattr = $mailto.mail
               
                if($mailattr -ne $null)
                {
                    SnapMail $mailto.mail $snap
                    Write-Host “$($mailto.mail) – $($snapdata.VM) and $($snap)”
                    Log -Path $Output -Value “MailTo Value : $($mailto.mail) : VMName : $($snapdata.VM) : SnapData : $($snap)”                   
                }
                else
                {
                    $FirstName = $mailTo.givenname
                    $LastName = $mailTo.sn
                    $FirstName = $FirstName -replace ” “, “”
                    $LastName = $LastName -replace ” “, “.”
                   
                    if($FirstName -eq $null)
                    {
                        $emailAddress = “steve@example.com”
                    }
                    else
                    {
                        $emailAddress = “$($FirstName).$($LastName)@example.com”
                    }
                   
                    SnapMail $emailAddress $snap
                    Write-Host “$($emailAddress) – $($snapdata.VM) and $($snap)”
                    Log -Path $Output -Value “EmailAddress : $($emailAddress) : VMName : $($snapdata.VM) : SnapData : $($snap)”
                    $emailAddress=””
                }
               
            $SnapCount++
            $SnapSpaceUsed += $snap.SizeGB
           
            Log -Path $Output -Value “Ending VM – $snapdata.vm”
            Log -Path $Output -Value “*************************************************”
           
            }
            else
            {
                Write-Host “Not sending email for $($snapdata) for $($snapdata.VM)”
                Log -Path $OutputErrors -Value ”    Not sending email for VMName : $($snapdata.VM) : SnapData : $($snap)”
            }
        }
    }
   
}

$SnapSpaceUsed = [Math]::Round($SnapSpaceUsed,2)
Log -path $Output -Value “Number of snaps notified via email : $($SnapCount)  `r`nAmount of spaced the snapshots consumed – $($SnapSpaceUsed) GB”

Disconnect-VIServer -Server $vCenterServer -Force -Confirm:$false
$EndDate = Get-Date
Log -Path $Output -Value “Ending process as $($Cred.Username) connecting to $($vCenterServer) at $($EndDate)”

Using CimCmdlets to connect to CIM provider with Powershell and VMware

I needed to retrieve hardware status on memory DIMM’s and used PowerCLI, Powershell to retrieve the data.  Note the script assumes you have a text file list of servers.

#Reference articles
#https://communities.vmware.com/thread/446593
#http://www.virtu-al.net/2012/10/29/using-powershell-v3-0-cim-cmdlets-with-vmware-esxi-hosts/
#http://technet.microsoft.com/en-us/library/jj553783.aspx
#http://www.powertheshell.com/additional-cim-cmdlets-for-download/
#http://blogs.msdn.com/b/powershell/archive/2012/08/24/introduction-to-cim-cmdlets.aspx
#Import-Module : The specified module ‘CimCmdlets’ was not loaded because no valid module file was found in any module directory.

#$Server = “esxiServer1”
#$HostUsername = “root”
#$HostUsername2=Get-Credential
#$CIOpt = New-CimSessionOption -SkipCACheck -SkipCNCheck -SkipRevocationCheck -Encoding Utf8 -UseSsl
#$Session = New-CimSession -Authentication Basic -Credential $HostUsername2 -ComputerName $Ipaddress -port 443 -SessionOption $CIOpt
#$Result = Get-CimInstance -CimSession $Session -ClassName CIM_Chip | where {$_.CreationClassName -eq “OMC_PhysicalMemory” } | Select Caption,DataWidth,FormFactor,MaxMemorySpeed,@{N=”CapacityGB”;E={[math]::Round($_.Capacity/1GB,0)}}

Import-module CimCmdlets

#$HostUsername = “root”
$HostUsername=Get-Credential

$serverlist=get-content -path ServerList.txt
foreach($server in $serverlist)
{
    $CIOpt = New-CimSessionOption -SkipCACheck -SkipCNCheck -SkipRevocationCheck -Encoding Utf8 -UseSsl
    $Session = New-CimSession -Authentication Basic -Credential $HostUsername -ComputerName $server -port 443 -SessionOption $CIOpt
    $Result = Get-CimInstance -CimSession $Session -ClassName CIM_Chip | where {$_.CreationClassName -eq “OMC_PhysicalMemory” } | Select Caption,DataWidth,FormFactor,MaxMemorySpeed,@{N=”CapacityGB”;E={[math]::Round($_.Capacity/1GB,0)}}

    foreach(  in $Result)
    {
        Write-Host $DIMM
    }
}