Add “Press any key to continue..” to a PowerShell script

From time to time it is nice to have a “Press any key to continue..” break point in a script to allow the user to review the status of an operation or just to add a user interaction to acknowledge the completion of an operation. This is especially useful when using a menu based script (see here) where the script will revert back to the menu once an operation is complete making it difficult to see the status of an operation when it completes or any Write-Host messages that may have been displayed. To get around this I use the following PowerShell Function to insert a “Press any key to continue..” break point that will wait for the user to…you guessed it…press the any key! 🙂

I use then when using a PowerShell Menu (See more about that here). You can edit the text in the quotes on line 3 to suite your use case. In my case i am calling the Menu function on line 5 so that when a user presses a key it will revert to the script menu. Simples!


Function anyKey
{
Write-Host -NoNewline -Object 'Press any key to return to the main menu...' -ForegroundColor Yellow
$null = $Host.UI.RawUI.ReadKey('NoEcho,IncludeKeyDown')
Menu
}


Use PowerCli to answer VM questions

Recently had a datastore in the lab briefly fill up and some VMs go into a suspended state awaiting a Retry/Cancel question to be answered. Rather than manually answer the question on each affected VM i looked to use PowerCli. With a little digging in the PowerCli documentation I found the Get-VMQuestion & Set-VMQuestion cmdlets. In an ideal world all you would need to do is this

Connect-VIServer -Server VC01
Get-VM | Get-VMQuestion | Set-VMQuestion -Option "Retry" -Confirm:$false
Disconnect-VIServer -Server VC01 -Confirm:$false
As i am using vCD all VMs have a UID appended with a space like this
IaaS01 (c4920308-f901-45be-b99c-0095175099e9)

So when you try to pass the VM name to Get-VMQuestion it does not handle the space in the VM name and the command fails. To get around this i created a simple script to first create an array of VM names thereby avoiding the issue with the space. Running the script below against my vCenter answered about 65 VM questions while i made a coffee!

Connect-VIServer -Server VC01
$vm = @()
$vm = Get-VM
$answerQuestion = $vm | Get-VMQuestion | Set-VMQuestion -Option "Retry" -Confirm:$false
Disconnect-VIServer -Server VC01 -Confirm:$false

Script to create vCenter clusters, switches, portgroups & VLANs

When setting up a new environment it is always good practice to plan out and document everything to avoid errors. From hostnames, cluster names, vDS names, portgroup names & VLANs etc. I was recently asked to help script the creation of the above from a csv to avoid fat fingers and to save time through automation. Enter PowerCli! Here is the script i created. Disclaimer: Its version 1…no error checking and can be made more efficient but it works and may be helpful to others! I’ve also posted it to GitHub with an example csv here https://github.com/LifeOfBrianOC/vmware_scripts


# Script to create vCenter Clusters, Distributed Switches & portgroups from CSV
# Edit the CSV location variable and the vCenter FQDN and run the script
# Tested with PowerCli 6.3.0
# This is version 1.0 There is no error checking in place so if an item
# already exists or cannot be found the script will error but should continue

#
$CSVPath = "C:\Scripts\Example.csv"
$vCenter = "vc01.domain.local"

#####################################
# DO NOT EDIT ANYTHING BELOW THIS LINE
#####################################

# Load VMware PowerCli Snapins
add-psSnapin VMWare* | out-null

# Connect to vCenter
Connect-VIserver $vCenter

# Get vCenter Datacenter Name
$datacenter = Get-Datacenter

@"
====================================
Creating Clusters
====================================
"@

# Import CSV and only read lines that have an entry in clusterName column
$csv = @()
$csv = Import-Csv -Path $CSVPath |
Where-Object -FilterScript {
$_.clusterName
}

# Loop through all _s in the CSV
ForEach ($_ in $csv)
{
New-Cluster -Location $datacenter -Name $_.clusterName -HAEnabled | out-null
}

@"
====================================
Creating Distributed Switches
====================================
"@

# Import CSV and only read lines that have an entry in switchName column
$csv = @()
$csv = Import-Csv -Path $CSVPath |
Where-Object -FilterScript {
$_.switchName
}

# Loop through all _s in the CSV
ForEach ($_ in $csv)
{
Import-Module VMware.VimAutomation.Vds
New-VDSwitch -Location $datacenter -Name $_.switchName -Mtu 1600 | out-null
}

@"
==========================================
Creating Distributed Switch Portgroups & Assigning VLANs
==========================================
"@

# Import CSV and only read lines that have an entry in portgroupName column
$csv = @()
$csv = Import-Csv -Path $CSVPath |
Where-Object -FilterScript {
$_.portgroupName
}

# Loop through all _s in the CSV
ForEach ($_ in $csv)
{
Import-Module VMware.VimAutomation.Vds
New-VDPortgroup -Name $_.portgroupName -VDSwitch $_.addToSwitch -VlanId $_.vlan | out-null
}
@"
==========================================
Setting Trunk Ports
==========================================
"@

# Import CSV and only read lines that have an entry in trunkPortgroup column
$csv = @()
$csv = Import-Csv -Path $CSVPath |
Where-Object -FilterScript {
$_.trunkPortgroup
}

# Loop through all _s in the CSV
ForEach ($_ in $csv)
{
Import-Module VMware.VimAutomation.Vds
Set-VDPortgroup $_.trunkPortgroup -VlanTrunkRange $_.trunkRange | out-null
}

@"
============================================
Disconnecting from vCenter....Done!
============================================
"@
# Disconnect vCenter
Disconnect-VIServer $vCenter

Find MoRef ID using powercli

From time to time you need to find the moref of an object in vCenter. This is a quick powercli one liner to get the name and ID of an object type (VM, Datastore etc)

This assumes you are already connected to vCenter using Connect-VIServer

# Datastores

Get-Datastore | Select Name,ID

This command returns output like below. In this example i have 1 datastore

Get-Datastore

#VMs

Get-VM | Select Name,ID

This command returns output like below

Get-VM

Use PowerCLI to patch multiple hosts

I was chatting to a friend who was looking to patch multiple (120) hosts with the same VIB and we discussed using PowerCLI to automate it. I did a quick google and didn’t find a script to do exactly what we needed. I did however find a script to do most of what we needed and modified it to do the VIB install and some basic logging! The script i used as a baseline is posted here on http://www.virtadmin.com/ Below is my modified version to install a VIB on each host before the reboot. ***As with any script please test extensively before running in a production environment.***

The full script can be found here

##############################################
# Script to patch multiple hosts with a VIB
# Inspired by http://www.virtadmin.com/rolling-reboot-vsphere-cluster-powercli/
# Hosts are listed in a text file
# Requires Powercli
##############################################
# Region User Variables

# Set vCenter Hostname Variable. 
# You will be asked for credentials when executing the script. e.g. "vc01.lab.local"
$vCenterServer = "ChangeME"

# Full Path to the text file with the list of ESXi hosts to be patched. 
# e.g. "C:\Scripts\VIHosts.txt"
$VIHosts = "ChangeME"

# Full path to the VIB to be installed. Use a common shared datastore. 
# Suggest a shared NFS/VMFS datastore. Copy the VIB to this location before starting. 
# e.g. "/vmfs/volumes/NFS_Shared/patch1/metadata.zip"
$vibPath = "ChangeME"

# Full path to where you would like the script to create a log. e.g. "C:\Scripts"
$LogDIR = "ChangeME"

# End Region User Variables

# DO NOT MODIFY BELOW THIS LINE!
##############################################

# Load VMware PowerCLI CmdLets
Add-PSSnapin vmware* | Out-Null

$TargetDIR = "$LogDIR\log"
if(!(Test-Path -Path $TargetDIR )){
    New-Item -ItemType directory -Path $TargetDIR
}

$Logfile = "$TargetDIR\patchESXi-Log.txt"
if(!(Test-Path -Path $Logfile )){
    New-Item -ItemType file -Path $Logfile
}

Function verifyTXTPath {
# Verify TXT Path
 if (!(Test-Path $VIHosts))  {
  Write-Host "ESXi Hosts File Not Found. Please verify the path and retry
						 " -ForegroundColor Red
	Write-Host -NoNewLine 'Press any key to exit...' -ForegroundColor Yellow;
$null = $Host.UI.RawUI.ReadKey('NoEcho,IncludeKeyDown');
Exit
		}
		  }


# Function to write Logfile entries
Function LogWrite
{
   Param ([string]$logstring)

   Add-content $Logfile -value $logstring
   }

   
# Connect to  the vCenter defined at the top of this script
Connect-VIServer -Server $vCenterServer | Out-Null

# Write progress to LogFile
LogWrite "$(Get-Date -Format "yyyy-MM-dd HH:mm:ss") Connected to vCenter $vCenterServer"
 
# Get ESXi hostname from txt file
$ESXiServers = Get-Content $VIHosts | %{Get-VMHost $_}
 
# Install ESXi Patch Function
Function PatchESXiServer ($CurrentServer) {
    # Get ESXi Server name
    $ServerName = $CurrentServer.Name
 
    # Put server in maintenance mode
    Write-Host "** Patching $ServerName **"
    Write-Host "Entering Maintenance Mode"
    Set-VMhost $CurrentServer -State maintenance -Evacuate | Out-Null
	
# Write progress to LogFile
LogWrite "$(Get-Date -Format "yyyy-MM-dd HH:mm:ss") Put ESXi host $CurrentServer in Maintenance Mode"
	
	# Install Patch
	Get-VMHost $CurrentServer | Install-VMHostPatch -Hostpath $vibPath

# Write progress to LogFile
LogWrite "$(Get-Date -Format "yyyy-MM-dd HH:mm:ss") Installing Patch on $CurrentServer"
 
    # Reboot host
    Write-Host "Rebooting $ServerName"
    Restart-VMHost $CurrentServer -confirm:$false | Out-Null

# Write progress to LogFile
LogWrite "$(Get-Date -Format "yyyy-MM-dd HH:mm:ss") Rebooting $CurrentServer"
	
    # Wait for Server to show as down
    do {
    sleep 15
    $ServerState = (get-vmhost $ServerName).ConnectionState
    }
    while ($ServerState -ne "NotResponding")
    Write-Host "$ServerName is Down"
 
    # Wait for server to reboot
    do {
    sleep 60
    $ServerState = (get-vmhost $ServerName).ConnectionState
    Write-Host "Waiting for $ServerName to Reboot ..."
    }
    while ($ServerState -ne "Maintenance")
    Write-Host "$ServerName is back up"
 
    # Exit maintenance mode
    Write-Host "Exiting Maintenance mode"
    Set-VMhost $CurrentServer -State Connected | Out-Null
    Write-Host "** Reboot Complete **"
    Write-Host ""
	
# Write progress to LogFile
LogWrite "$(Get-Date -Format "yyyy-MM-dd HH:mm:ss") Exit ESXi host $CurrentServer from Maintenance Mode"
	
}
 
## MAIN
verifyTXTPath
foreach ($ESXiServer in $ESXiServers) {
PatchESXiServer ($ESXiServer)
}
 
# Disconnect from vCenter
Disconnect-VIServer -Server $vCenterServer -Confirm:$False

# Write progress to LogFile
LogWrite "$(Get-Date -Format "yyyy-MM-dd HH:mm:ss") Disconnect from vCenter $vCenterServer"

vCD vApp Report

Here is a quick one liner for reporting on vCD vApps. This is useful for quickly auditing deployed vApps in vCD. I used the Search-Cloud cmdlet as its lighter and faster than Get-CIvApp. I’m sorting by created date and outputting Created date, vApp Name, Power state, and vApp Owner and outputting the results to a csv file

 

Search-Cloud -QueryType AdminVApp | Sort-Object -Property CreationDate | select CreationDate,Name,Status,OwnerName | Out-File C:\vCDAudit.csv