diff --git a/vCenter Tools/CreatedvSwitchPorts.ps1 b/vCenter Tools/CreatedvSwitchPorts.ps1 new file mode 100644 index 0000000..444f961 --- /dev/null +++ b/vCenter Tools/CreatedvSwitchPorts.ps1 @@ -0,0 +1,226 @@ +param ( + [string]$vCenter = "", + [string]$dvSwitch = "", + [string]$CSVFile = "", + [switch]$SaveCred = $false, + [switch]$Help = $false +) + +# Variables +$fileVersion = "1.0.0" +$fileDate = "2026-01-28" + +# Display Script Banner +Write-Host "--------------------------------------------------" -ForegroundColor White +Write-Host "dvSwitch Port Group Creation Script" -ForegroundColor Green +Write-Host "Version $($FileVersion) ($($FileDate))" -ForegroundColor Green +Write-Host "Written by Charlie Silverman" -ForegroundColor Green +Write-Host "--------------------------------------------------" -ForegroundColor White + +if ($Help) { + Write-Host + Write-Host "Purpose: Create dvSwitch Port Groups en masse." + Write-Host "Command Syntax:" + Write-Host " CreatedvSwitchPortGroups.ps1 (-vcenter [vcenter host name]) (-dvSwitch ""[dvswitch]"") (-CSVFile ""[csvfile]"")" + Write-Host " (-savecred) (-help)`n" -foregroundcolor green + Write-Host "Command Line Options:" + Write-Host + Write-Host -NoNewLine " -vCenter " -foregroundcolor Green + Write-Host "- The FQDN or IP address of a vCenter server." + Write-Host -NoNewLine " -dvSwitch " -foregroundcolor Green + Write-Host "- The name of the distributed virtual switch in vCenter." + Write-Host -NoNewLine " -CSVfile " -foregroundcolor Green + Write-Host "- The path to the CSV file. See note below for format." + Write-Host -NoNewLine " -savecred " -foregroundcolor Green + Write-Host "- Configure and store vCenter server credentials. This will only store the credentials under" + Write-Host " the current logged in user." + Write-Host -NoNewLine " -help " -foregroundcolor Green + Write-Host "- Display command help." + Write-Host + Write-Host "If any of the information is not provided, you will be prompted. If a credential has been previously stored, it will" + Write-Host "be used again unless the -savecred option is specified." + Write-Host + Write-Host "The CSV file must follow the format listed below. The only field required is PortGroup. Fields can be in any order." + Write-Host " PortGroup, VLAN, PortBinding, NumPorts, ActiveUplink, DisabledUplink, StandbyUplink, Notes" + Write-Host "Where:" + Write-Host " Portgroup " -ForegroundColor Green -NoNewLine + Write-Host " - The name of the port group." + Write-Host " VLAN " -ForegroundColor Green -NoNewLine + Write-Host " - [Optional] The VLAN tag for the port group." + Write-Host " PortBinding " -ForegroundColor Green -NoNewLine + Write-Host " - [Optional] The type of port binding for the port group. This can be either Static or Dynamic." + Write-Host " Defaults to Static if not specified." + Write-Host " NumPorts " -ForegroundColor Green -NoNewLine + Write-Host " - [Optional] The number of ports for the port group. This is ignored for a dynamic port group." + Write-Host " Defaults to 254 if not specified." + Write-Host " ActiveUplink " -ForegroundColor Green -NoNewLine + Write-Host " - [Optional] A comma delimited list of switch uplinks to be set as active for the port group." + Write-Host " StandbyUplink " -ForegroundColor Green -NoNewLine + Write-Host " - [Optional] A comma delimited list of switch uplinks to be set as standby for the port group." + Write-Host " UnusedUplink " -ForegroundColor Green -NoNewLine + Write-Host " - [Optional] A comma delimited list of switch uplinks to be set as unused for the port group." + Write-Host " Notes " -ForegroundColor Green -NoNewLine + Write-Host " - [Optional] Notes to add to the port group." + + exit +} + +$quiet = Set-PowerCLIConfiguration -InvalidCertificateAction Ignore -Confirm:$false + +if ($vCenter -eq "") { + $vCenter = Read-Host "Enter name of vCenter Server" +} + +if ($SaveCred) { + $credential = Get-Credential -Message "Enter credentials for vCenter server $vCenter" + $credential | Export-Clixml -path $env:USERPROFILE\kistools-$vcenter.cred + Write-Host "Saving credentials." +} + +if (Test-Path -path $env:USERPROFILE\kistools-$vcenter.cred -PathType Leaf) { + $credentials = Import-Clixml -path $env:USERPROFILE\kistools-$vcenter.cred + Write-Host "Using stored credentials" +} else { + $credentials = Get-Credential -Message "Enter credentials for vCenter server $vCenter" + if (!$credentials) { + Write-Host "ERROR: No credentials were entered" -foregroundcolor yellow + } +} + +try { + Write-Host -NoNewLine "Connecting to vCenter $vcenter..." + $vcconn = Connect-VIServer $vcenter -Credential $credentials -ErrorAction Stop + Write-Host " Success" -ForegroundColor green +} catch { + Write-Host " Failure" -ForegroundColor Yellow -BackgroundColor Red + exit +} + +try { + if ($dvSwitch -ne "") { + get-VDswitch $dvSwitch -ErrorAction Stop + } +} catch { + Write-Host "ERROR: dvSwitch ""$dvSwitch"" is not found." -ForegroundColor Red + $dvSwitch = "" +} + +if ($dvSwitch -eq "") { + $dvSwitchlist = Get-VDSwitch | Sort-Object + While (($dvSwitch -eq "") -or (-not $dvSwitch)) { + Write-Host "`ndvSwitch Selection:`n" + for ($x = 0; $x -lt $dvSwitchlist.length; $x++) { + Write-Host "$(([string]($x+1)).PadLeft(3,' '))" -NoNewLine -ForegroundColor Green + Write-Host ": " -NoNewLine -ForegroundColor Gray + Write-Host "$($DVSwitchlist[$x].name)" -ForegroundColor Yellow + } + $dvSwitchNum = [int](Read-Host "`nSelect dvSwitch Number") - 1 + $dvSwitch = $dvSwitchlist[$dvSwitchNum] + } + $mySwitch = get-VDSwitch $dvSwitch +} else { + $mySwitch = get-VDSwitch $dvSwitch +} + +$csvdata = Import-CSV $csvfile + +Write-Host "Processing Port Group List" + +for ($x = 0; $x -lt $csvdata.length; $x++) { + $pgname = $csvdata[$x].PortGroup + if ($pgname -eq "") { continue } + if (-not ($pgvlan = $csvdata[$x].VLAN)) { + $pgvlan = 0 + } + if (-not ($pgbinding = $csvdata[$x].PortBinding)) { + $pgbinding = "Static" + } + if (-not ($pgports = $csvdata[$x].NumPorts)) { + $pgports = 254 + } + if ($pgactive -and (($pgactive = ($csvdata[$x].ActiveUplink).split(",")).count -eq 0)) { + $pgactive = @() + } + if ($pgstandby -and (($pgstandby = ($csvdata[$x].StandbyUplink).split(",")).count -eq 0)) { + $pgstandby = @() + } + if ($pgunused -and (($pgunused = ($csvdata[$x].UnusedUplink).split(",")).count -eq 0)) { + $pgunused = @() + } + Write-Host "$pgname... " -NoNewLine + if ($temp = Get-VdPortGroup $pgname -erroraction silentlycontinue) { + Write-Host " Already Exists - Skipping" -ForegroundColor Yellow + continue + } + try { + $newpg = New-VdPortGroup -name "$pgname" -vlanid $pgvlan -PortBinding $pgbinding -VdSwitch "$dvSwitch" -erroraction stop + Write-Host "Created Successfully" -ForegroundColor Green + } catch { + Write-Host " ERROR - Unable to Create Port Group $pgname on dvSwitch $dvSwitch." -ForegroundColor Red + Write-Host "$($_.Exception.Message)" -ForegroundColor Gray + continue + } + + $myuplinks = @() + foreach($uplink in $pgactive) { + if ($uplink -eq "") { continue } + if (($mySwitch.ExtensionData.Config.UplinkPortPolicy.UplinkPortName | Where-Object {$_ -eq $uplink}).Count -eq 0) { + Write-Host "Warning: Uplink ""$uplink"" for Active is not found on switch $dvSwitch" -ForegroundColor Yellow + continue + } + $myuplinks += $uplink + } + + if ($myuplinks.Count -gt 0) { + Write-Host " Assigning active uplink ports... " -NoNewLine + try { + $temp = $newpg | Get-VDUplinkTeamingPolicy | Set-VDUplinkTeamingPolicy -ActiveUplinkPort $myuplinks -ErrorAction Stop + Write-Host "Success" -ForegroundColor Green + } catch { + Write-Host "ERROR assigning uplinks to $pgname" -ForegroundColor Red + Write-Host "$($_.Exception.Message)" -ForegroundColor Gray + } + } + + $myuplinks = @() + foreach($uplink in $pgstandby) { + if ($uplink -eq "") { continue } + if (($mySwitch.ExtensionData.Config.UplinkPortPolicy.UplinkPortName | Where-Object {$_ -eq $uplink}).Count -eq 0) { + Write-Host "Warning: Uplink ""$uplink"" for STANDBY is not found on switch $dvSwitch" -ForegroundColor Yellow + continue + } + $myuplinks += $uplink + } + + if ($myuplinks.Count -gt 0) { + Write-Host " Assigning standby uplink ports... " -NoNewLine + try { + $temp = $newpg | Get-VDUplinkTeamingPolicy | Set-VDUplinkTeamingPolicy -StandbyUplinkPort $myuplinks -ErrorAction Stop + Write-Host "Success" -ForegroundColor Green + } catch { + Write-Host "ERROR assigning standby uplinks to $pgname" -ForegroundColor Red + Write-Host "$($_.Exception.Message)" -ForegroundColor Gray + } + } + + $myuplinks = @() + foreach($uplink in $pgunused) { + if ($uplink -eq "") { continue } + if (($mySwitch.ExtensionData.Config.UplinkPortPolicy.UplinkPortName | Where-Object {$_ -eq $uplink}).Count -eq 0) { + Write-Host "Warning: Uplink ""$uplink"" for UNUSED is not found on switch $dvSwitch" -ForegroundColor Yellow + continue + } + $myuplinks += $uplink + } + + if ($myuplinks.Count -gt 0) { + Write-Host " Assigning unused uplink ports... " -NoNewLine + try { + $temp = $newpg | Get-VDUplinkTeamingPolicy | Set-VDUplinkTeamingPolicy -UnusedUplinkPort $myuplinks -ErrorAction Stop + Write-Host "Success" -ForegroundColor Green + } catch { + Write-Host "ERROR assigning unused uplinks to $pgname" -ForegroundColor Red + Write-Host "$($_.Exception.Message)" -ForegroundColor Gray + } + } +} \ No newline at end of file diff --git a/vCenter Tools/DRSGroupFunctions.ps1 b/vCenter Tools/DRSGroupFunctions.ps1 new file mode 100644 index 0000000..14298ef --- /dev/null +++ b/vCenter Tools/DRSGroupFunctions.ps1 @@ -0,0 +1,118 @@ +Function Get-DrsGroup { +<# +.SYNOPSIS +Retrieves DRS groups from a cluster. + +.DESCRIPTION +Retrieves DRS groups from a cluster. + +.PARAMETER Cluster +Specify the cluster for which you want to retrieve the DRS groups + +.PARAMETER Name +Specify the name of the DRS group you want to retrieve. + +.EXAMPLE +Get-DrsGroup -Cluster $Cluster -Name "VMs DRS Group" +Retrieves the DRS group "Vms DRS Group" from cluster $Cluster. + +.EXAMPLE +Get-Cluster | Get-DrsGroup +Retrieves all the DRS groups for all clusters. + +.INPUTS +ClusterImpl + +.OUTPUTS +ClusterVmGroup +ClusterHostGroup + +.COMPONENT +VMware vSphere PowerCLI +#> + + param([parameter(Mandatory=$true, ValueFromPipeline=$true)]$Cluster, + [string] $Name="*") + + process { + $Cluster = Get-Cluster -Name $Cluster + if($Cluster) { + $Cluster.ExtensionData.ConfigurationEx.Group | ` + Where-Object {$_.Name -like $Name} + } + } +} + +Function Add-VMToDrsGroup { +<# +.SYNOPSIS +Adds a virtual machine to a cluster VM DRS group. + +.DESCRIPTION +Adds a virtual machine to a cluster VM DRS group. + +.PARAMETER Cluster +Specify the cluster for which you want to retrieve the DRS groups + +.PARAMETER DrsGroup +Specify the DRS group you want to retrieve. + +.PARAMETER VM +Specify the virtual machine you want to add to the DRS Group. + +.EXAMPLE +Add-VMToDrsGroup -Cluster $Cluster -DrsGroup "VM DRS Group" -VM $VM +Adds virtual machine $VM to the DRS group "VM DRS Group" of cluster $Cluster. + +.EXAMPLE +Get-Cluster MyCluster | Get-VM "A*" | Add-VMToDrsGroup -Cluster MyCluster -DrsGroup $DrsGroup +Adds all virtual machines with a name starting with "A" in cluster MyCluster to the DRS group $DrsGroup of cluster MyCluster. + +.INPUTS +VirtualMachineImpl + +.OUTPUTS +Task + +.COMPONENT +VMware vSphere PowerCLI +#> + + param([parameter(Mandatory=$true)] $Cluster, + [parameter(Mandatory=$true)] $DrsGroup, + [parameter(Mandatory=$true, ValueFromPipeline=$true)] $VM) + + begin { + $Cluster = Get-Cluster -Name $Cluster + } + + process { + if ($Cluster) { + if ($DrsGroup.GetType().Name -eq "string") { + $DrsGroupName = $DrsGroup + $DrsGroup = Get-DrsGroup -Cluster $Cluster -Name $DrsGroup + } + if (-not $DrsGroup) { + Write-Error "The DrsGroup $DrsGroupName was not found on cluster $($Cluster.name)." + } + else { + if ($DrsGroup.GetType().Name -ne "ClusterVmGroup") { + Write-Error "The DrsGroup $DrsGroupName on cluster $($Cluster.Name) doesn't have the required type ClusterVmGroup." + } + else { + $VM = $Cluster | Get-VM -Name $VM + If ($VM) { + $spec = New-Object VMware.Vim.ClusterConfigSpecEx + $spec.groupSpec = New-Object VMware.Vim.ClusterGroupSpec[] (1) + $spec.groupSpec[0] = New-Object VMware.Vim.ClusterGroupSpec + $spec.groupSpec[0].operation = "edit" + $spec.groupSpec[0].info = $DrsGroup + $spec.groupSpec[0].info.vm += $VM.ExtensionData.MoRef + + $Cluster.ExtensionData.ReconfigureComputeResource_Task($spec, $true) + } + } + } + } + } +} diff --git a/vCenter Tools/DeployTestingVMs.ps1 b/vCenter Tools/DeployTestingVMs.ps1 new file mode 100644 index 0000000..9b9b1d9 --- /dev/null +++ b/vCenter Tools/DeployTestingVMs.ps1 @@ -0,0 +1,675 @@ +param ( + [string]$vCenter = "", + [string]$Cluster = "", + [string]$dvSwitch = "", + [string]$Datastore = "", + [string]$SourceVMName = "", + [string]$Folder = "", + [string]$CSVFile = "", + [switch]$SaveCred = $false, + [switch]$Help = $false +) + +Import-Module .\DRSGroupFunctions.ps1 + +function GetFolderFromPath($folder) { + $parts = $folder.Split("\") + if ($parts.length -eq 0) { return $folder } + for ($x = 0; $x -lt $parts.length; ++$x) { + $thispart = $parts[$x] + if ($x -gt 0) { + $thisfolder = Get-Folder $parts[$x] | Where-Object {$_.Parent.Id -eq $parent.Id} + } else { + $thisfolder = Get-Folder $parts[$x] | Where-Object {$_.Parent.Name -eq "vm"} + } + $parent = $thisfolder + } + return $thisfolder +} + +# Variables +$fileVersion = "1.2.0" +$fileDate = "2026-06-03" + +# Display Script Banner +Write-Host "--------------------------------------------------" -ForegroundColor White +Write-Host "vSphere Testing VM Deployment Script" -ForegroundColor Green +Write-Host "Version $($FileVersion) ($($FileDate))" -ForegroundColor Green +Write-Host "Written by Charlie Silverman" -ForegroundColor Green +Write-Host "--------------------------------------------------" -ForegroundColor White + +if ($Help) { + Write-Host + Write-Host "Purpose: Deploy testing VMs for a set of port groups." + Write-Host "Command Syntax:" + Write-Host " DeployTestingVMs.ps1 (-vcenter [vCenter]) (-Cluster ""[Cluster]"") (-dvSwitch ""[dvswitch]"")" -ForegroundColor green + Write-Host " (-SourceVMName ""[SourceVMName]"") (-CSVFile ""[csvfile]"")" -ForegroundColor green + Write-Host "Command Line Options:" + Write-Host + Write-Host -NoNewLine " -vCenter " -ForegroundColor Green + Write-Host "- The FQDN or IP address of a vCenter server." + Write-Host -NoNewLine " -Cluster " -ForegroundColor Green + Write-Host "- The name of the Cluster in vCenter." + Write-Host -NoNewLine " -dvSwitch " -ForegroundColor Green + Write-Host "- The name of the distributed virtual switch in vCenter." + Write-Host -NoNewLine " -SourceVMName " -ForegroundColor Green + Write-Host "- The name of the distributed virtual switch in vCenter." + Write-Host -NoNewLine " -CSVfile " -ForegroundColor Green + Write-Host "- The path to the CSV file. See note below for format." + Write-Host -NoNewLine " -savecred " -ForegroundColor Green + Write-Host "- Configure and store vCenter server credentials. This will only store the credentials under" + Write-Host " the current logged in user." + Write-Host -NoNewLine " -help " -ForegroundColor Green + Write-Host "- Display command help." + Write-Host + Write-Host "If any of the information is not provided, you will be prompted. If a credential has been previously stored, it will" + Write-Host "be used again unless the -savecred option is specified." + Write-Host + Write-Host "The CSV file must follow the format listed below. The only field required is PortGroup. Fields can be in any order." + Write-Host " VLAN, Description, IPAddress, Netmask, Gateway, Site" + Write-Host "Where:" + Write-Host " VLAN " -ForegroundColor Green -NoNewLine + Write-Host " - The VLAN tag for the port group." + Write-Host " Description " -ForegroundColor Green -NoNewLine + Write-Host " - The Description for the port group." + Write-Host " IPAddress " -ForegroundColor Green -NoNewLine + Write-Host " - IP address for the VM." + Write-Host " Netmask " -ForegroundColor Green -NoNewLine + Write-Host " - Netmask for the network." + Write-Host " Gateway " -ForegroundColor Green -NoNewLine + Write-Host " - Default gateway for the network." + Write-Host " Site " -ForegroundColor Green -NoNewLine + Write-Host " - The site prefix to be used for DRS group configuration." + Write-Host "" + Write-Host "The port group name will be assumed to be in the following format: ""[Description] - VLAN[VLAN]""" + Write-Host "If the VLAN tag is 12 and the description is ""Big Servers"", the port group name will be ""Big Servers - VLAN12""" + Write-Host "" + Write-Host "The DRS group name will be assumed to be in the following format: ""[Site] Only VMs""" + Write-Host "If the Site is Primary, the DRS group name will be ""Primary Only VMs""" + Write-Host "" + Write-Host "Additionally, the new VM names will be in the format ""KISStress - VLAN[VLAN]""" + exit +} + +try { + $data = import-csv $CSVFile +} catch { + Write-Host "ERROR: Input file is not in a valid format." -ForegroundColor Yellow -BackgroundColor Red + Write-Host "Message: $($_.Exception.Message)" -ForegroundColor yellow + exit +} + +if (!($data | Where-Object {$_.PSObject.Properties.Name -contains "VLAN"}) -or (!($data | Where-Object {$_.PSObject.Properties.Name -contains "Description"}))) { + Write-Host "ERROR: Input file is not in a valid format." -ForegroundColor Yellow -BackgroundColor Red + exit +} + +$setip = $false + +if ($data | Where-Object {$_.PSObject.Properties.Name -contains "IPAddress"}) { + if ($data | Where-Object {$_.PSObject.Properties.Name -contains "Netmask"}) { + if ($data | Where-Object {$_.PSObject.Properties.Name -contains "Gateway"}) { + $setip = $true + } + } +} + +if ($vCenter -eq "") { + $vCenter = Read-Host "Enter name of vCenter Server" +} + +if ($SaveCred) { + $credential = Get-Credential -Message "Enter credentials for vCenter server $vCenter" + $credential | Export-Clixml -path $env:USERPROFILE\kistools-$vcenter.cred + Write-Host "Saving credentials." +} + +if (Test-Path -path $env:USERPROFILE\kistools-$vcenter.cred -PathType Leaf) { + $credentials = Import-Clixml -path $env:USERPROFILE\kistools-$vcenter.cred + Write-Host "Using stored credentials" +} else { + $credentials = Get-Credential -Message "Enter credentials for vCenter server $vCenter" + if (!$credentials) { + Write-Host "ERROR: No credentials were entered" -ForegroundColor Yellow -BackgroundColor Red -NoNewLine + Write-Host "" + Write-Host "Operation Terminated" -ForegroundColor Yellow + exit + } +} + +try { + Write-Host -NoNewLine "Connecting to vCenter $vcenter... " + $vcconn = Connect-VIServer $vcenter -Credential $credentials -ErrorAction Stop + Write-Host "Success" -ForegroundColor green +} catch { + Write-Host "Failure" -ForegroundColor Yellow -BackgroundColor Red -NoNewLine + Write-Host "" + Write-Host "Message: $($_.Exception.Message)" -ForegroundColor yellow + Write-Host "Operation Terminated" -ForegroundColor Yellow + exit +} + +if ($Cluster -eq "") { + $Clusterlist = Get-Cluster | Sort-Object + $startpos = 0 + $step = 10 + While (($Cluster -eq "") -or (-not $Cluster)) { + $endpos = $startpos + 10; + if ($endpos -gt $Clusterlist.length) { $endpos = $Clusterlist.length } + $page = $startpos / $step + 1 + $maxpage = [math]::Ceiling($Clusterlist.length / $step) + Write-Host "`nCluster Selection (Page $page of $maxpage):`n" + if ($startpos -gt 0) { + Write-Host " 0" -NoNewLine -ForegroundColor Green + Write-Host ": " -NoNewLine -ForegroundColor Gray + Write-Host "<< Previous Page" -ForegroundColor White + } + for ($x = $startpos; $x -lt $endpos; $x++) { + Write-Host "$(([string]($x+1-$startpos)).PadLeft(3,' '))" -NoNewLine -ForegroundColor Green + Write-Host ": " -NoNewLine -ForegroundColor Gray + Write-Host "$($Clusterlist[$x].name)" -ForegroundColor Yellow + } + if ($endpos -lt $Clusterlist.length) { + Write-Host " $($step+1)" -NoNewLine -ForegroundColor Green + Write-Host ": " -NoNewLine -ForegroundColor Gray + Write-Host ">> Next Page" -ForegroundColor White + } + $Clusternum = Read-Host "`nSelect Cluster Number" + $Clusternum = [int]$Clusternum - 1 + if ($Clusternum -eq -1) { + $startpos -= $step + continue + } + if ($Clusternum -eq $step) { + $startpos += $step + continue + } + $Cluster = $Clusterlist[$Clusternum + $startpos].Name + } +} + +try { + Write-Host "Validating cluster ""$Cluster""... " -NoNewLine + $mycluster = get-cluster $Cluster + Write-Host "Success" -ForegroundColor Green +} catch { + Write-Host "Failure" -ForegroundColor Yellow -BackgroundColor Red + Write-Host "ERROR: Unable to locate cluster ""$Cluster"" in $vCenter" -ForegroundColor Yellow -BackgroundColor Red -NoNewLine + Write-Host "" + Write-Host "Operation Terminated" -ForegroundColor Yellow + exit +} + +if ($dvSwitch -eq "") { + $myhost = $mycluster | Get-VMHost | Select-Object -First 1 + $SwitchList = Get-VdSwitch -VMHost $myhost + $startpos = 0 + $step = 10 + While (($dvSwitch -eq "") -or (-not $dvSwitch)) { + $endpos = $startpos + 10; + if ($endpos -gt $SwitchList.length) { $endpos = $SwitchList.length } + $page = $startpos / $step + 1 + $maxpage = [math]::Ceiling($SwitchList.length / $step) + Write-Host "`ndvSwitch Selection (Page $page of $maxpage):`n" + if ($startpos -gt 0) { + Write-Host " 0" -NoNewLine -ForegroundColor Green + Write-Host ": " -NoNewLine -ForegroundColor Gray + Write-Host "<< Previous Page" -ForegroundColor White + } + for ($x = $startpos; $x -lt $endpos; $x++) { + Write-Host "$(([string]($x+1-$startpos)).PadLeft(3,' '))" -NoNewLine -ForegroundColor Green + Write-Host ": " -NoNewLine -ForegroundColor Gray + Write-Host "$($SwitchList[$x].name)" -ForegroundColor Yellow + } + if ($endpos -lt $SwitchList.length) { + Write-Host " $($step+1)" -NoNewLine -ForegroundColor Green + Write-Host ": " -NoNewLine -ForegroundColor Gray + Write-Host ">> Next Page" -ForegroundColor White + } + $dvSwitchnum = Read-Host "`nSelect dvSwitch Number" + $dvSwitchnum = [int]$dvSwitchnum - 1 + if ($dvSwitchnum -eq -1) { + $startpos -= $step + continue + } + if ($dvSwitchnum -eq $step) { + $startpos += $step + continue + } + $dvSwitch = $SwitchList[$dvSwitchnum + $startpos].Name + } +} + +try { + Write-Host "Validating dvSwitch ""$dvSwitch""... " -NoNewLine + $mydvSwitch = Get-VdSwitch $dvSwitch + Write-Host "Success" -ForegroundColor Green +} catch { + Write-Host "Failure" -ForegroundColor Yellow -BackgroundColor Red + Write-Host "ERROR: Unable to locate dvSwitch $dvSwitch in $vCenter" -ForegroundColor Yellow -BackgroundColor Red -NoNewLine + Write-Host "" + exit +} + +if ($SourceVMName -eq "") { + $VMList = $mycluster | Get-VM + $startpos = 0 + $step = 10 + While (($SourceVMName -eq "") -or (-not $SourceVMName)) { + $endpos = $startpos + 10; + if ($endpos -gt $VMList.length) { $endpos = $VMList.length } + $page = $startpos / $step + 1 + $maxpage = [math]::Ceiling($VMList.length / $step) + Write-Host "`nSource VM Selection (Page $page of $maxpage):`n" + if ($startpos -gt 0) { + Write-Host " 0" -NoNewLine -ForegroundColor Green + Write-Host ": " -NoNewLine -ForegroundColor Gray + Write-Host "<< Previous Page" -ForegroundColor White + } + for ($x = $startpos; $x -lt $endpos; $x++) { + Write-Host "$(([string]($x+1-$startpos)).PadLeft(3,' '))" -NoNewLine -ForegroundColor Green + Write-Host ": " -NoNewLine -ForegroundColor Gray + Write-Host "$($VMlist[$x].name)" -ForegroundColor Yellow + } + if ($endpos -lt $VMList.length) { + Write-Host " $($step+1)" -NoNewLine -ForegroundColor Green + Write-Host ": " -NoNewLine -ForegroundColor Gray + Write-Host ">> Next Page" -ForegroundColor White + } + $VMnum = Read-Host "`nSelect VM Number" + $VMnum = [int]$VMnum - 1 + if ($VMnum -eq -1) { + $startpos -= $step + continue + } + if ($VMnum -eq $step) { + $startpos += $step + continue + } + $SourceVMName = $VMlist[$VMnum + $startpos].Name + } +} + +try { + Write-Host "Locating source VM ""$SourceVMName""... " -NoNewLine + if ($abyss = Get-VM $SourceVMName -ErrorAction Stop) { + Write-Host "Success" -ForegroundColor Green + } else { + Write-Host "Failure" -ForegroundColor Yellow -BackgroundColor Red -NoNewLine + Write-Host "" + Write-Host "Operation Aborted" -ForegroundColor Yellow + } +} catch { + Write-Host "Failure" -ForegroundColor Yellow -BackgroundColor Red + Write-Host "ERROR: Error locating VM $SourceVMName in $vCenter" -ForegroundColor Yellow -BackgroundColor Red -NoNewLine + Write-Host "" + Write-Host "Operation Terminated" -ForegroundColor Yellow + exit +} + +if ($Datastore -eq "") { + $DSList = $mycluster | Get-Datastore | Where-Object {$_.ExtensionData.Host.Count -gt 1} + $startpos = 0 + $step = 10 + While (($Datastore -eq "") -or (-not $Datastore)) { + $endpos = $startpos + 10; + if ($endpos -gt $DSList.length) { $endpos = $DSList.length } + $page = $startpos / $step + 1 + $maxpage = [math]::Ceiling($DSList.length / $step) + Write-Host "`nDatastore Selection (Page $page of $maxpage):`n" + if ($startpos -gt 0) { + Write-Host " 0" -NoNewLine -ForegroundColor Green + Write-Host ": " -NoNewLine -ForegroundColor Gray + Write-Host "<< Previous Page" -ForegroundColor White + } + for ($x = $startpos; $x -lt $endpos; $x++) { + Write-Host "$(([string]($x+1-$startpos)).PadLeft(3,' '))" -NoNewLine -ForegroundColor Green + Write-Host ": " -NoNewLine -ForegroundColor Gray + Write-Host "$($DSlist[$x].name)" -ForegroundColor Yellow + } + if ($endpos -lt $DSList.length) { + Write-Host " $($step+1)" -NoNewLine -ForegroundColor Green + Write-Host ": " -NoNewLine -ForegroundColor Gray + Write-Host ">> Next Page" -ForegroundColor White + } + $dvSwitchnum = Read-Host "`nSelect Datastore Number" + $dvSwitchnum = [int]$dvSwitchnum - 1 + if ($dvSwitchnum -eq -1) { + $startpos -= $step + continue + } + if ($dvSwitchnum -eq $step) { + $startpos += $step + continue + } + $Datastore = $DSlist[$dvSwitchnum + $startpos].Name + } +} + +try { + Write-Host "Locating Datastore ""$Datastore""... " -NoNewLine + $mydatastore = Get-Datastore $Datastore -ErrorAction Stop + if ($mydatastore) { + Write-Host "Success" -ForegroundColor Green + } else { + Write-Host "Failure" -ForegroundColor Yellow -BackgroundColor Red + Write-Host "ERROR: Unable to locate datastore $Datastore in $Cluster" -ForegroundColor Yellow -BackgroundColor Red -NoNewLine + Write-Host "" + Write-Host "Operation Terminated" -ForegroundColor Yellow + } +} catch { + Write-Host "Failure" -ForegroundColor Yellow -BackgroundColor Red + Write-Host "ERROR: Error locating Datastore $Datastore in $vCenter" -ForegroundColor Yellow -BackgroundColor Red -NoNewLine + Write-Host "" + Write-Host "Operation Terminated" -ForegroundColor Yellow + exit +} + +if ($Folder -eq "") { + Write-Host "Inventorying VM Folder Structure..." -NoNewLine + $FolderList = Get-Folder | Where-Object {$_.Parent.Name -eq "vm"} | Sort-Object -Property @{Expression={$_.Name}; Descending=$false} + $FolderList += Get-Folder | Where-Object {($_.Parent.Name -ne "vm") -and ($_.Type -eq "VM") -and $($_.Name -ne "vm")} | Sort-Object -Property @{Expression={$_.Parent.Name}; Descending=$false},@{Expression={$_.Name}; Descending=$false} + Write-Host " Success" -ForegroundColor Green + for($x = 0; $x -lt $FolderList.length; ++$x) { + $thisfolder = $FolderList[$x] + $path = "" + if ($thisfolder.Parent.Name -ne "vm") { + $searchcount = 0 + While ($searchcount -lt 10) { + $nextfolder = $thisfolder.Parent + $path = "$($nextfolder.Name)\$($path)" + if ($nextfolder.Parent.Name -ne "vm") { + $thisfolder = $nextfolder + ++$searchcount + } else { + $searchcount = 11 + } + } + } + if ($path -ne "") { + $path = "$($path)$($Folderlist[$x].Name)" + } else { + $path = $Folderlist[$x].Name + } + + $FolderList[$x] | Add-Member -NotePropertyName "FullPath" -NotePropertyValue $path + } + $startpos = 0 + $step = 10 + + While (-not $myfolder) { + $endpos = $startpos + 10; + if ($endpos -gt $FolderList.length) { $endpos = $FolderList.length } + $page = $startpos / $step + 1 + $maxpage = [math]::Ceiling($FolderList.length / $step) + Write-Host "`nFolder Selection (Page $page of $maxpage):`n" + if ($startpos -gt 0) { + Write-Host " 0" -NoNewLine -ForegroundColor Green + Write-Host ": " -NoNewLine -ForegroundColor Gray + Write-Host "<< Previous Page" -ForegroundColor White + } + for ($x = $startpos; $x -lt $endpos; $x++) { + $path = $Folderlist[$x].FullPath + Write-Host "$(([string]($x+1-$startpos)).PadLeft(3,' '))" -NoNewLine -ForegroundColor Green + Write-Host ": " -NoNewLine -ForegroundColor Gray + Write-Host "$path" -ForegroundColor Yellow + } + if ($endpos -lt $FolderList.length) { + Write-Host " $($step+1)" -NoNewLine -ForegroundColor Green + Write-Host ": " -NoNewLine -ForegroundColor Gray + Write-Host ">> Next Page" -ForegroundColor White + } + $Foldernum = Read-Host "`nSelect Folder Number" + $Foldernum = [int]$Foldernum - 1 + if ($Foldernum -eq -1) { + $startpos -= $step + continue + } + if ($Foldernum -eq $step) { + $startpos += $step + continue + } + + $Folder = $FolderList[$Foldernum].FullPath + $myfolder = $FolderList[$Foldernum].Name + } +} + +try { + Write-Host "Locating Folder ""$Folder""... " -NoNewLine + $myfolder = GetFolderFromPath($Folder) + if ($myfolder) { + Write-Host "Success" -ForegroundColor Green + } else { + Write-Host "Failure" -ForegroundColor Yellow -BackgroundColor Red -NoNewLine + Write-Host "" + Write-Host "Operation Terminated" -ForegroundColor Yellow + } +} catch { + Write-Host "Failure" -ForegroundColor Yellow -BackgroundColor Red + Write-Host "ERROR: Error locating Folder $Folder in $vCenter" -ForegroundColor Yellow -BackgroundColor Red -NoNewLine + Write-Host "" + Write-Host "Operation Terminated" -ForegroundColor Yellow + exit +} + +## Validate that the proper DRS groups exist for each site in the format "[site] Only VMs" +Write-Host "Verifying that the necessary DRS groups exist..." +$sites = $data.Site | Select-Object -Unique -CaseInsensitive + +$siteerror = $false +foreach ($site in $sites) { + $sitegroup = "$site Only VMs" + Write-Host " - $sitegroup... " -NoNewLine + try { + $drsg = Get-DrsGroup -Cluster $Cluster -Name $sitegroup + if ($drsg) { + Write-Host "Success" -ForegroundColor Green + } else { + Write-Host "Failure" -ForegroundColor Yellow -BackgroundColor Red -NoNewLine + Write-Host "" + $siteerror = $true + } + } catch { + Write-Host "Failure" -ForegroundColor Yellow -BackgroundColor Red -NoNewLine + Write-Host "ERROR: Unable to locate DRS group $sitegroup." -ForegroundColor Yellow -BackgroundColor Red -NoNewLine + Write-Host "" + $siteerror = $true + } +} + +if ($siteerror) { + Write-Host "Operation Terminated" -ForegroundColor Yellow + exit +} + +## Validate that the necessary network port groups exist in the format "[Description] - VLAN[VLAN]" +Write-Host "Verifying that the necessary port groups exist..." +$pgerror = $false +foreach ($row in $data) { + $vlan = [int]$row.VLAN + $description = $row.Description + $myportgroupname = "$description - VLAN$vlan" + Write-Host " - $myportgroupname... " -NoNewLine + try { + $pg = Get-VDPortGroup -Name $myportgroupname + if ($pg) { + Write-Host "Success" -ForegroundColor Green + } else { + Write-Host "Failure" -ForegroundColor Yellow -BackgroundColor Red + $pgerror = $true + } + } catch { + Write-Host "Failure" -ForegroundColor Yellow -BackgroundColor Red + $pgerror = $true + } +} + +if ($pgerror) { + Write-Host "Operation Terminated" -ForegroundColor Yellow + exit +} + +exit + +foreach($row in $data) { + try { + $abyss = [int]$row.VLAN + } catch { + continue + } + $vlan = [int]$row.VLAN + $description = $row.Description + + $myportgroupname = "$description - VLAN$vlan" + $myvmname = "KISStress - VLAN$vlan" + + Write-Host + + try { + if (Get-VM $myvmname -ErrorAction Silent) { + Write-Host "WARNING: VM ""$myvmname"" already exists. Skipping." -ForegroundColor Yellow + continue + } + } catch { + Write-Host "ERROR: Unable to determine if ""$myvmname"" exists. Skipping." -ForegroundColor Red + continue + } + + try { + $myportgroup = Get-VdPortGroup -Name $myportgroupname -VDSwitch $dvswitch + } catch { + Write-Host "ERROR: Could not locate port group ""$myportgroupname""" + continue + } + + Write-Host "Cloning ""$SourceVMName"" to VM ""$myvmname""... " -NoNewLine + try { + $task = New-VM -Name $myvmname -VM $SourceVMName -ResourcePool $mycluster -Datastore $Datastore -Location $myfolder -DiskStorageFormat Thin -ErrorAction Silent -RunAsync + } catch { + Write-Host "Failure" -ForegroundColor Yellow -BackgroundColor Red + Write-Host "Message: $($_.Exception.Message)" -ForegroundColor yellow + continue + } + + $created = $false + while (($task = Get-Task -iD $task.Id).State -eq "Running") { + Start-Sleep -Seconds 5 + Write-Host "." -NoNewLine + } + switch(($task = Get-Task -iD $task.Id).State) { + "Success" { + Write-Host "Success" -ForegroundColor Green + } + "Error" { + if ($task.ExtensionData.Info.Cancelled) { + Write-Host "Failure - Task Cancelled" -ForegroundColor Yellow -BackgroundColor Red + continue + } else { + Write-Host "Failure - Unknown Error" -ForegroundColor Yellow -BackgroundColor Red + continue + } + } + } + $nic = Get-VM $myvmname | Get-NetworkAdapter + try { + Write-Host "Changing NIC on VM ""$myvmname"" to port group ""$myportgroupname""... " -NoNewLine + $task = $nic | Set-NetworkAdapter -Portgroup $myportgroup -Confirm:$false -ErrorAction Stop -RunAsync + while (($task = Get-Task -iD $task.Id).State -eq "Running") { + Start-Sleep -Seconds 1 + Write-Host "." -NoNewLine + } + switch(($task = Get-Task -iD $task.Id).State) { + "Success" { + Write-Host "Success" -ForegroundColor Green + } + "Error" { + if ($task.ExtensionData.Info.Cancelled) { + Write-Host "Failure - Task Cancelled" -ForegroundColor Yellow -BackgroundColor Red + } else { + Write-Host "Failure - Unknown Error" -ForegroundColor Yellow -BackgroundColor Red + } + } + } + } catch { + Write-Host " Failure" -ForegroundColor Yellow -BackgroundColor Red + Write-Host "Message: $($_.Exception.Message)" -ForegroundColor yellow + } + + if ($row.Site) { + Write-Host "Adding VM ""$myvmname"" to DRS Group ""$($row.Site) Only VMs"" in cluster $Cluster... " -NoNewLine + try { + $task = Add-VMToDrsGroup -Cluster $Cluster -DrsGroup "$($row.Site) Only VMs" -VM $myvmname + $task | Add-Member -NotePropertyName "Id" -NotePropertyValue "Task-$($task.Value)" + } catch { + Write-Host "Failure" -ForegroundColor Yellow -BackgroundColor Red + Write-Host "ERROR: Error adding VM ""$myvmname"" to DRS Group ""$($row.Site) Only VMs""" -ForegroundColor Yellow -BackgroundColor Red + Write-Host "Message: $($_.Exception.Message)" -ForegroundColor yellow + continue + } + + while (($task = Get-Task -iD $task.Id).State -eq "Running") { + Start-Sleep -Seconds 1 + Write-Host "." -NoNewLine + } + switch(($task = Get-Task -iD $task.Id).State) { + "Success" { + Write-Host "Success" -ForegroundColor Green + } + "Error" { + if ($task.ExtensionData.Info.Cancelled) { + Write-Host "Failure - Task Cancelled" -ForegroundColor Yellow -BackgroundColor Red + } else { + Write-Host "Failure - Unknown Error" -ForegroundColor Yellow -BackgroundColor Red + } + } + } + } + + try { + Write-Host "Powering on VM ""$myvmname""... " -NoNewLine + $task = Start-VM $myvmname -ErrorAction Stop -RunAsync + } catch { + Write-Host "Failure" -ForegroundColor Yellow -BackgroundColor Red + Write-Host "Message: $($_.Exception.Message)" -ForegroundColor yellow + } + + $booted = $false + while (!$booted) { + if ((Get-VM $myvmname).ExtensionData.Guest.ToolsStatus -eq "toolsOk") { + $booted = $true + continue + } + Start-Sleep 5 + Write-Host "." -NoNewLine + } + Write-Host "Success" -ForegroundColor Green + +# Update Network Information If Provided + if ($setip) { + $ipaddress = $row.IPAddress + $netmask = $row.netmask + $gateway = $row.Gateway + + if (!$ipaddress -or !$netmask -or !$gateway) { continue } + Write-Host "Updating network configuration for vm ""$myvmname""... " -NoNewLine + + $ifcfg = "sudo echo 'STARTMODE=auto' > /etc/sysconfig/network/ifcfg-eth0 +sudo echo 'BOOTPROTO=static' >> /etc/sysconfig/network/ifcfg-eth0 +sudo echo 'IPADDR=$ipaddress' >> /etc/sysconfig/network/ifcfg-eth0 +sudo echo 'NETMASK=$netmask' >> /etc/sysconfig/network/ifcfg-eth0 +sudo echo 'default $gateway - -' > /etc/sysconfig/network/routes +sudo /etc/init.d/network restart +" + $UserName = "stress" + $Password = ConvertTo-SecureString -String "stress" -AsPlainText -Force + $Credential = New-Object System.Management.Automation.PSCredential -ArgumentList $UserName, $Password + try { + $abyss = Invoke-VMScript $myvmname -ScriptText $ifcfg -GuestCredential $Credential -ScriptType Bash + Write-Host "Success" -ForegroundColor Green + } catch { + Write-Host "Failure" -ForegroundColor Yellow -BackgroundColor Red + Write-Host "Message: $($_.Exception.Message)" -ForegroundColor yellow + } + } +} \ No newline at end of file diff --git a/vCenter Tools/GetKeys.ps1 b/vCenter Tools/GetKeys.ps1 new file mode 100644 index 0000000..f62e9e9 --- /dev/null +++ b/vCenter Tools/GetKeys.ps1 @@ -0,0 +1,150 @@ +param ( + [string]$vCenter = "", + [switch]$SaveCred = $false, + [switch]$Help = $false +) + +# Variables +$fileVersion = "1.1.0" +$fileDate = "2025-04-02" + +# Display Script Banner +Write-Host "--------------------------------------------------" -ForegroundColor White +Write-Host "vSphere TPM Key Inventory" -ForegroundColor Green +Write-Host "Version $($FileVersion) ($($FileDate))" -ForegroundColor Green +Write-Host "Written by Charlie Silverman" -ForegroundColor Green +Write-Host "--------------------------------------------------" -ForegroundColor White + +if ($Help) { + Write-Host + Write-Host "Purpose: Collect TPM encryption keys for all ESXi hosts." + Write-Host "Command Syntax:" + Write-Host " GetKeys.ps1 (-vcenter [vcenter host name]) (-savecred) (-help)" + Write-Host "Command Line Options:" + Write-Host + Write-Host -NoNewLine " -vCenter " -foregroundcolor Green + Write-Host "- The FQDN or IP address of a vCenter server." + Write-Host -NoNewLine " -savecred " -foregroundcolor Green + Write-Host "- Configure and store vCenter server credentials. This will only store the credentials under" + Write-Host " the current logged in user." + Write-Host -NoNewLine " -help " -foregroundcolor Green + Write-Host "- Display command help." + Write-Host + Write-Host "If no any of the information is not provided, you will be prompted. If a credential has been previously stored, it will be used again unless the -savecred option is specified." + Write-Host + exit +} + +if (-not (Get-Module -ListAvailable -Name VMware.PowerCLI*)) { + Write-Host "ERROR: VMware PowerCLI is not installed!" -foregroundcolor yellow + exit +} + +$hideresult = Set-PowerCLIConfiguration -InvalidCertificateAction Ignore -Confirm:$false + +if ($vCenter -eq "") { + $vCenter = Read-Host "Enter name of vCenter Server" +} + +if ($savecred) { + $credential = Get-Credential -Message "Enter credentials for vCenter server $vcenter" + $credential | Export-Clixml -path $env:USERPROFILE\kis-getkeys-$vcenter.cred + Write-Host "Saving credentials." +} + +if (Test-Path -path $env:USERPROFILE\kis-getkeys-$vcenter.cred -PathType Leaf) { + $credentials = Import-Clixml -path $env:USERPROFILE\kis-getkeys-$vcenter.cred + Write-Host "Using stored credentials" +} else { + $credentials = Get-Credential -Message "Enter credentials for vCenter server $vcenter" + if (!$credentials) { + Write-Host "ERROR: No credentials were entered" -foregroundcolor yellow + } +} + +try { + Write-Host -NoNewLine "Connecting to vCenter $vcenter..." + $vcconn = Connect-VIServer $vcenter -Credential $credentials -ErrorAction SilentlyContinue + Write-Host " Success" -ForegroundColor green +} catch { + Write-Host " Failure" -ForegroundColor Yellow -BackgroundColor Red + exit +} + +$now = (Get-Date).ToString("yyyyMMdd_hhmm") +$exportfile = "TPMKeys-$vCenter-$now.csv" + +$VMHosts = Get-VMHost | Sort-Object +$VMHostKeys = @() +foreach ($VMHost in $VMHosts) { + $esxcli = Get-EsxCli -VMHost $VMHost -V2 + try { + $encryption = $esxcli.system.settings.encryption.get.Invoke() + if ($encryption.Mode -eq "TPM") + { + $key = $esxcli.system.settings.encryption.recovery.list.Invoke() + $hostKey = [pscustomobject]@{ + Host = $VMHost.Name + EncryptionMode = $encryption.Mode + RequireExecutablesOnlyFromInstalledVIBs = $encryption.RequireExecutablesOnlyFromInstalledVIBs + RequireSecureBoot = $encryption.RequireSecureBoot + RecoveryID = $key.RecoveryID + RecoveryKey = $key.Key + } + $VMHostKeys += $hostKey + } + else + { + $hostKey = [pscustomobject]@{ + Host = $VMHost.Name + EncryptionMode = $encryption.Mode + RequireExecutablesOnlyFromInstalledVIBs = $encryption.RequireExecutablesOnlyFromInstalledVIBs + RequireSecureBoot = $encryption.RequireSecureBoot + RecoveryID = $null + RecoveryKey = $null + } + $VMHostKeys += $hostKey + } + } + catch { + $VMHost.Name + $_ + } +} +Write-Host "Exporting TPM keys for all hosts in $vcenter`n to $exportfile..." -NoNewLine +try { + $VMHostKeys | Export-Csv $exportfile + Write-Host " Complete" -ForegroundColor Green +} catch { + Write-Host " Failed" -ForegroundColor Red + exit +} + +Write-Host "Attempting to reset all TPM Encryption Recovery Key Backup Alarms..." +# Locate TPM Encrytpion Recovery Key Backup Alarm ID +$alarm = Get-AlarmDefinition "TPM Encryption Recovery Key Backup Alarm" +$alarmID = $alarm.Id -split '-', 2 | select-object -last 1 +$alarmMgr = Get-View AlarmManager + +foreach ($VMHost in $VMHosts) { + try { + $decalarm = $VMHost.ExtensionData.TriggeredAlarmState | where-object {$_.Alarm.Value -eq $alarmID} | Select-Object -first 1 + if ([string]::IsNullorEmpty($decalarm)) { + continue + } + } catch { + Write-Host "Error retrieving alarms for host $VMHost" + continue + } +} + +Write-Host "Clearing all yellow alarms from ESXi hosts..." +try { + $filter = New-Object VMware.Vim.AlarmFilterSpec + $filter.Status += [VMware.Vim.ManagedEntityStatus]::yellow + $filter.TypeEntity = [VMware.Vim.AlarmFilterSpecAlarmTypeByEntity]::entityTypeHost + $filter.TypeTrigger = [vmware.vim.AlarmFilterSpecAlarmTypeByTrigger]::triggerTypeAll + $alarmMgr.ClearTriggeredAlarms($filter) + Write-Host " Success" -ForegroundColor Green +} catch { + Write-Host " Failed" -ForegroundColor Red +} diff --git a/vCenter Tools/GetSnaps.ps1 b/vCenter Tools/GetSnaps.ps1 new file mode 100644 index 0000000..194b9e3 --- /dev/null +++ b/vCenter Tools/GetSnaps.ps1 @@ -0,0 +1,114 @@ +param ( + [string]$vcenter = "", + [switch]$allvms = $false, + [switch]$SaveCred = $false, + [switch]$Help = $false +) + +# Variables +$fileVersion = "1.0.0" +$fileDate = "2024-12-11" +$savecred = $false + +# Display Script Banner +Write-Host "--------------------------------------------------" -ForegroundColor White +Write-Host "vCenter Snapshot Report" -ForegroundColor Green +Write-Host "Version $($FileVersion) ($($FileDate))" -ForegroundColor Green +Write-Host "Written by Charlie Silverman" -ForegroundColor Green +Write-Host "--------------------------------------------------" -ForegroundColor White + +if ($Help) { + Write-Host "Command Syntax:" -ForegroundColor Green + Write-Host 'GetSnaps.ps1 -vcenter="VCURI" [-help] [-savecred]' -ForegroundColor Green + Write-Host "" + Write-Host "Command line options:" -ForegroundColor Green + Write-Host "---------------------" -ForegroundColor Green + Write-Host "-vcenter - Specify the FQDN for the vCenter server" -ForegroundColor Green + Write-Host "-allvms - Get snapshots for all VMs. Omitting this option will only check powered on VMs." -ForegroundColor Green + Write-Host "-savecred - Save Credentials. If credentials were previously saved and this option is not selected," -ForegroundColor Green + Write-Host " they will be used." -ForegroundColor Green + Write-Host "-help - Show this help information." -ForegroundColor Green + exit +} + +if ($vcenter -eq "") { + $vcenter = Read-Host "Enter name of vCenter Server" +} + +try { + if (Test-Path -Path "$env:USERPROFILE\GetSnaps-${vcenter}.cred" -PathType Leaf) { + Write-Host "Using stored credentials for $vcenter." + $cred = Import-CliXML -path "$env:USERPROFILE\GetSnaps-${vcenter}.cred" + } else { + $cred = Get-Credential -Message "Enter credentials for vCenter $vcenter" + } + $vcenterConnection = connect-viserver $vcenter -credential $cred -ErrorAction Stop +} catch { + Write-Host "ERROR: Invalid credentials or incorrect vCenter FQDN" + Write-Host $_ + exit +} +if ($savecred) { + Write-Host "Storing credentials for $vcenter." + $cred | Export-CliXML -Path "$env:USERPROFILE\GetSnaps-${vcenter}.cred" +} + +$DesktopPath = [Environment]::GetFolderPath("Desktop") +$now = (Get-Date).ToString("yyyyMMdd_hhmm") +$outputFileName = "$DesktopPath\VMSnapshotList-$now.html" + +# Get all VMs +if ($allvms) { + $VMs = Get-VM +} else { + $VMs = Get-VM | Where {$_.PowerState -eq "PoweredOn"} +} + +# Create empty array to hold snapshot information +$SnapshotInfo = @() + +# Loop through each VM and get snapshot information +foreach ($VM in $VMs) { + $Snapshots = Get-Snapshot -VM $VM + + # Loop through each snapshot and add information to array + foreach ($Snapshot in $Snapshots) { + $SnapshotInfo += [PSCustomObject]@{ + VMName = $VM.Name + SnapshotName = [System.Uri]::UnescapeDataString($Snapshot.Name) + Created = $Snapshot.Created + Description = $Snapshot.Description + SizeGB = [math]::Round(($Snapshot.SizeGB),2) + } + } +} + +# Create HTML output +$HTML = @" + + +Snapshots Report + + + +"@ +$HTML += $SnapshotInfo | ConvertTo-Html -Fragment -Property VMName, SnapshotName, Created, Description, SizeGB +$HTML += @" + + +"@ + +# Output HTML to file +$HTML | Out-File -FilePath $outputFileName + +# Output confirmation message +Write-Host "Generating Snapshots Report" +Write-Host "Your snapshots report has been saved to: $outputFileName" + +# Disconnect from vCenter Server +Disconnect-VIServer -Confirm:$false \ No newline at end of file diff --git a/vCenter Tools/GetVMNetworks.ps1 b/vCenter Tools/GetVMNetworks.ps1 new file mode 100644 index 0000000..1fda415 --- /dev/null +++ b/vCenter Tools/GetVMNetworks.ps1 @@ -0,0 +1,105 @@ +param ( + [string]$vcenter = "", +# [switch]$allvms, + [switch]$savecred, + [switch]$Help = $false +) + +# Variables +$fileVersion = "1.2.0" +$fileDate = "2024-12-13" + +# Display Script Banner +Write-Host "--------------------------------------------------" -ForegroundColor White +Write-Host "vSphere VM Network Assignment Export" -ForegroundColor Green +Write-Host "Version $($FileVersion) ($($FileDate))" -ForegroundColor Green +Write-Host "Written by Charlie Silverman" -ForegroundColor Green +Write-Host "--------------------------------------------------" -ForegroundColor White + +if ($Help) { + Write-Host "Command Syntax:" -ForegroundColor Green + Write-Host 'GetVMNetworks.ps1 -vcenter="VCURI" [-help] [-savecred]' -ForegroundColor Green + Write-Host "" + Write-Host "Command line options:" -ForegroundColor Green + Write-Host "---------------------" -ForegroundColor Green + Write-Host "-vcenter - Specify the URI for the vCenter server" -ForegroundColor Green + Write-Host "-savecred - Save Credentials. If credentials were previously saved and this option is not selected," -ForegroundColor Green + Write-Host " they will be used." -ForegroundColor Green + Write-Host "-help - Show this help information." -ForegroundColor Green + exit +} + + +if ($vcenter -eq "") { + $vcenter = Read-Host "Enter name of vCenter Server" +} + +try { + if (Test-Path -Path "$env:USERPROFILE\GetVMNetworks-$vcenter.cred" -PathType Leaf) { + Write-Host "Using stored credentials for $vcenter." + $cred = Import-CliXML -path "$env:USERPROFILE\GetVMNetworks-$vcenter.cred" + } else { + $cred = Get-Credential -Message "Enter credentials for vCenter $vcenter" + } + $vcenterConnection = connect-viserver $vcenter -credential $cred -ErrorAction Stop +} catch { + Write-Host "ERROR: Invalid credentials or incorrect vCenter FQDN" + Write-Host $_ + exit +} +if ($savecred) { + Write-Host "Storing credentials for $vcenter." + $cred | Export-CliXML -Path "$env:USERPROFILE\GetVMNetworks-$vcenter.cred" +} + +# Output filename +$DesktopPath = [Environment]::GetFolderPath("Desktop") +$now = (Get-Date).ToString("yyyyMMdd_hhmm") +$csvFileName = "$DesktopPath\VMNetworkList-$now.csv" + +# Find the maximum number of adapters in a VM to create the CSV header properly +$allvms = get-vm +$maxnics = 0 +foreach ($vm in $allvms) { + $adaptercount = ($vm | get-networkadapter | measure).Count + if ($adaptercount -gt $maxnics) { + $maxnics = $adaptercount + } +} + +# Write the CSV header +$header = """Cluster"",""VM Name""" +for ($x = 1; $x -le $maxnics; ++$x) { + #write-host -NoNewLine ",Network Adapter $x" + $header = "$header,""Network Adapter $x""" +} +# write-host +$header | Out-File -FilePath $csvFileName -Encoding UTF8 + +# Get list of clusters +$clusters = get-cluster + +# Iterate through cluster list +foreach ($cluster in $clusters) { + # Get list of VMs in the cluster + $vms = $cluster | get-vm | sort-object -Property Name + $clusterName = $cluster.Name + # Iterate through the VM list + foreach($vm in $vms) { + $vmName = $vm.Name + $entry = """$cluster"",""$vmName""" + # Get all network adapters for the VM + $netAdapters = $vm | get-networkadapter + # Iterate through adapter list + foreach ($adapter in $netAdapters) { + # Append port group name for each adapter + $adapterName = $adapter.Name + $adapterPG = $adapter.NetworkName + $entry = "$entry,""$adapterPG""" + } + # Write host entry + #write-Host $entry + $entry | Out-File -FilePath $csvFileName -Append -Encoding UTF8 + } + Write-Host "`nData for cluster $clusterName has been saved to the file $csvFileName." +} \ No newline at end of file diff --git a/vCenter Tools/GetVMUpgradeInfo.ps1 b/vCenter Tools/GetVMUpgradeInfo.ps1 new file mode 100644 index 0000000..90597b8 --- /dev/null +++ b/vCenter Tools/GetVMUpgradeInfo.ps1 @@ -0,0 +1,291 @@ +param ( + [string]$vCenter = "", + [string]$cluster = "", + [string]$filename = "vmexport.xlsx", + [string]$Customer = "", + [switch]$Help = $false, + [switch]$SaveCred = $false +) + +# Variables +$fileVersion = "1.0.0" +$fileDate = "2026-05-21" +$scriptName = Split-Path -Path $PSCommandPath -Leaf + +# Display Script Banner +Write-Host "--------------------------------------------------" -ForegroundColor White +Write-Host "Collect VM Information for Upgrades" -ForegroundColor Green +Write-Host "Version $($FileVersion) ($($FileDate))" -ForegroundColor Green +Write-Host "Written by Charlie Silverman" -ForegroundColor Green +Write-Host "--------------------------------------------------" -ForegroundColor White + +function CheckModuleInstalled($moduleName, $command) { + Write-Host "Checking that package $moduleName is installed..." -NoNewLine + if (-not(get-command $command -erroraction silentlycontinue)) { + Write-Host " Missing" -ForegroundColor Red + if (-not (Get-Module -ListAvailable -Name $moduleName)) { + $response = "" + while (($response -ne "yes") -and ($response -ne "no")) { + $response = (Read-Host "ImportExcel module not instaled. Install now? (yes/no)").ToLower() + } + if ($response -eq "no") { + Write-Host "Operation Cancelled." -ForegroundColor Yellow + exit + } + Write-Host "Installing PowerShell module $moduleName..." -NoNewLine + Install-Module -Name $moduleName -Scope CurrentUser -Force + Write-Host " Success" -ForegroundColor Green + } + Write-Host "Loading PowerShell module $moduleName..." -NoNewLine + Import-Module $moduleName + Write-Host " Success" -ForegroundColor Green + } else { + Write-Host " Found" -ForegroundColor Green + } +} + +# Check for ImportExcel and install if missing +CheckModuleInstalled -moduleName "VCF.PowerCLI" -command "connect-viserver" +CheckModuleInstalled -moduleName "ImportExcel" -command "import-excel" + +$moduleName = "ImportExcel" +Import-Module ImportExcel + +if ($Help) { + Write-Host + Write-Host "Purpose: Generate a spreadsheet for VM Upgrades." + Write-Host "Command Syntax:" + Write-Host " $scriptName (-vcenter [vcenter host name]) (-cluster ""[cluster]"") (-filename ""[filename]"") (-customer ""[customer]""" -foregroundcolor green + Write-Host "Command Line Options:" + Write-Host + Write-Host -NoNewLine " -vCenter " -foregroundcolor Green + Write-Host "- The FQDN or IP address of a vCenter server." + Write-Host -NoNewLine " -cluster " -foregroundcolor Green + Write-Host "- The name of the cluster as shown in vCenter." + Write-Host -NoNewLine " -customer " -foregroundcolor Green + Write-Host "- The name of the customer to display on the sheet title." + Write-Host -NoNewLine " -filename " -foregroundcolor Green + Write-Host "- The CSV filename to save the result." + Write-Host + Write-Host "If any of the information is not provided, you will be prompted. If a credential has been previously stored, it will" + Write-Host "be used again unless the -savecred option is specified." + Write-Host + Write-Host "If a filename is not specified, the file vmexport.csv will be created in the current directory." + Write-Host + + exit +} + +$timestamp = Get-Date -Format "yyyy-MM-dd HH:mm:ss" + +$quiet = Set-PowerCLIConfiguration -InvalidCertificateAction Ignore -Confirm:$false + +if ($vCenter -eq "") { + $vCenter = Read-Host "Enter name of vCenter Server" +} + +if ($SaveCred) { + $credential = Get-Credential -Message "Enter credentials for vCenter server $vCenter" + $credential | Export-Clixml -path $env:USERPROFILE\kistools-$vcenter.cred + Write-Host "Saving credentials." +} + +if (Test-Path -path $env:USERPROFILE\kistools-$vcenter.cred -PathType Leaf) { + $credentials = Import-Clixml -path $env:USERPROFILE\kistools-$vcenter.cred + Write-Host "Using stored credentials" +} else { + $credentials = Get-Credential -Message "Enter credentials for vCenter server $vCenter" + if (!$credentials) { + Write-Host "ERROR: No credentials were entered" -foregroundcolor yellow + } +} + +if (Test-Path -path $filename -PathType Leaf) { + Write-Host "File $filename already exists." -ForegroundColor Yellow + exit +} + +try { + Write-Host -NoNewLine "Connecting to vCenter $vcenter..." + $vcconn = Connect-VIServer $vcenter -Credential $credentials -ErrorAction SilentlyContinue + Write-Host " Success" -ForegroundColor green +} catch { + Write-Host " Failure" -ForegroundColor Yellow -BackgroundColor Red + exit +} + +if ($cluster -eq "") { + $Clusterlist = Get-Cluster | Sort-Object + While (($cluster -eq "") -or (-not $cluster)) { + Write-Host "`nCluster Selection:`n" + for ($x = 0; $x -lt $Clusterlist.length; $x++) { + Write-Host "$(([string]($x+1)).PadLeft(3,' '))" -NoNewLine -ForegroundColor Green + Write-Host ": " -NoNewLine -ForegroundColor Gray + Write-Host "$($Clusterlist[$x].name)" -ForegroundColor Yellow + } + $Clusternum = Read-Host "`nSelect Cluster Number" + if ($Clusternum -as [int]) { + $Clusternum = [int]$Clusternum - 1 + $cluster = $Clusterlist[$Clusternum] + } + } + $mycluster = get-cluster $cluster +} else { + $mycluster = get-cluster $cluster +} + +# Collect base VMware Tools and Hardware version from first Host +$myhost = $mycluster | get-vmhost | select-object -first 1 +$esxcli = Get-EsxCLI -VMHost $myhost -V2 +$latestHW = ($myhost | Get-View).Capability.MaxSupportedVmVersion +$latestVMT = ($esxcli.software.vib.list.invoke() | Where-Object {$_.ID -match "VMware_locker_tools"}).Version + +$latestHW +$latestVMT + +exit + +Write-Host "Gathering information about VMs in cluster $cluster..." -NoNewLine +$vmlist = $mycluster | get-vm +$vmcount = $vmlist.length + +$vmresponse = @() + +for ($x = 0; $x -lt $vmlist.length; $x++) { + $percent = [Math]::Round(($x / $vmlist.length) * 100,0) + Write-Progress -ID 1 -Activity "Collecting VM Information" -Status "Processing VM $($x+1) of $($vmlist.length)" -PercentComplete $percent + $thisvm = $vmlist[$x]; + $thisvmguest = $thisvm | get-vmguest + $vmname = $thisvm.Name; + $vmhardwareversion = $thisvmguest.ExtensionData.HwVersion + $vmtoolsstatus = $thisvmguest.ExtensionData.ToolsRunningStatus + $vmtoolsversionstatus = $thisvmguest.ExtensionData.ToolsVersionStatus + $vmosname = $thisvmguest.OSFullName + $vmtoolsversion = $thisvmguest.ToolsVersion + + $badvmnics = ($thisvm | get-NetworkAdapter | Where-Object {$_.Type -ne "Vmxnet3"}).length -gt 0 + + $thisvmobj = [PSCustomObject]@{ + "Name" = $vmname + "VM Hardware Version" = $vmhardwareversion + "VMTools Status" = $vmtoolsstatus + "VMTools Version" = $vmtoolsversion + "VMTools Version Status" = $vmtoolsversionstatus + "Has Non vmxnet3 NICs" = $badvmnics + "VMHW Updated" = $null + "VMTools Updated" = $null + "In Prog" = "" + "Migrating" = "" + "Complete" = "" + } + + $vmresponse += $thisvmobj +} +Write-Host "Disconnecting from vCenter $vcenter..." -NoNewLine +try { + $quiet = Disconnect-VIServer $vcenter -Confirm:$false +} catch { + Write-Host " FAILURE" -ForegroundColor Yellow - BackgroundColor Red +} +Write-Host " Success" -ForegroundColor Green +Write-Progress -ID 1 -Completed +Write-Host " Success" -ForegroundColor Green +Write-Host "Writing information about $($vmresponse.length) VMs to file $filename..." -NoNewLine +try { + $sheetTitle = "VM Export for Cluster $cluster - $timestamp" + if ($customer -ne "") { + $sheetTitle = "$customer - $sheetTitle" + } + $exceltable = $vmresponse | Export-Excel -path $filename -WorksheetName "vCenter VM Data" -PassThru -Autosize -FreezeTopRow ` + -TableName "VMData" -TableStyle Medium15 -Title $sheetTitle -TitleBold -TitleSize 22 ` + -TableTotalSettings @{ CPU = "Sum"; RAM = "Sum"; "Provisioned Storage" = "Sum"; "Used Space Original" = "Sum" } +} catch { + Write-Host + Write-Host "ERROR! Unable to create file $filename" -ForegroundColor Yellow + Write-Host "Error: $($_.Exception.Message)" -ForegroundColor Red -BackgroundColor Yellow +} +try { + $sheet = $exceltable.Workbook.Worksheets["vCenter VM Data"] + $sheet.Column(3) | Set-ExcelRange -NFormat '#,##0 " GB"' + $sheet.Column(4) | Set-ExcelRange -NFormat '#,##0.00 " GB"' + $sheet.Column(5) | Set-ExcelRange -NFormat '#,##0.00 " GB"' + $sheet.Column(6) | Set-ExcelRange -NFormat '#,##0.00 " GB"' + Set-Format -Address $sheet.Cells["2:2"] -WrapText + $sheet.InsertColumn(1,1) + $sheet.Column(1).Width = 2 + $sheet.Cells["I:R"].Style.HorizontalAlignment = "Center" + $sheet.Cells["C2:G2"].Style.HorizontalAlignment = "Center" + $sheet.Column(5).Width = 13 + $sheet.Column(6).Width = 13 + $sheet.Column(7).Width = 13 + $sheet.Column(13).Width = 12 + $sheet.Column(14).Width = 12 + $sheet.Column(15).Width = 12 + $sheet.Column(16).Width = 12 + $sheet.Column(17).Width = 12 + $sheet.Column(18).Width = 12 + For ($counter = 3; $counter -lt 3 + $vmcount; ++$counter) { + $PFormula = '=IF(AND(COUNTIF(M' + $counter + ':O' + $counter + ',"X")<3,COUNTIF(M' + $counter + ':O' + $counter + ',"")<3),"X","")' + Set-ExcelRange -Address $sheet.Cells["P" + $counter] -Formula $PFormula + $QFormula = '=IF(OR(M' + $counter + '="O",N' + $counter + '="O",O' + $counter + '="O"),"X","")' + Set-ExcelRange -Address $sheet.Cells["Q" + $counter] -Formula $QFormula + $RFormula = '=IF(AND(M' + $counter + '="X",N' + $counter + '="X",O' + $counter + '="X"),"X","")' + Set-ExcelRange -Address $sheet.Cells["R" + $counter] -Formula $RFormula + $HFormula = '=if(AND($G' + $counter + '>0,$G' + $counter + '<>""), $F' + $counter + ',"")' + Set-ExcelRange -Address $sheet.Cells["H" + $counter] -Formula $HFormula + } + $datarange = "B3:R" + (2+$vmcount) + Add-ConditionalFormatting -Worksheet $sheet -Address $datarange -RuleType Expression -ConditionValue '=INDIRECT("VMData[@Disk Format]")="Thick"' -BackgroundColor Red -ForegroundColor White + Add-ConditionalFormatting -Worksheet $sheet -Address $datarange -RuleType Expression -ConditionValue '=INDIRECT("VMData[@Disk Format]")="O"' -BackgroundColor Orange + Add-ConditionalFormatting -Worksheet $sheet -Address $datarange -RuleType Expression -ConditionValue '=INDIRECT("VMData[@In Prog]")="X"' -BackgroundColor Yellow + Add-ConditionalFormatting -Worksheet $sheet -Address $datarange -RuleType Expression -ConditionValue '=INDIRECT("VMData[@Complete]")="X"' -BackgroundColor Green + $sheet.Cells["S4"].Value = "Total VMs:" + $sheet.Cells["S5"].Value = "Thick Provisioned:" + $sheet.Cells["S6"].Value = "Converting to Thin:" + $sheet.Cells["S7"].Value = "Partially Migrated:" + $sheet.Cells["S8"].Value = "In Progress:" + $sheet.Cells["S9"].Value = "Completed:" + $sheet.Cells["S10"].Value = "Remaining:" + $sheet.Cells["S12"].Value = "Original Space Usage:" + $sheet.Cells["S13"].Value = "New Space Usage:" + $sheet.Cells["S14"].Value = "Space Saved:" + $sheet.Cells["S15"].Value = "Savings:" + $sheet.Cells["S4:S15"].Style.HorizontalAlignment = "Right" + $sheet.Column(19).Width = 22 + Set-ExcelRange -Address $sheet.Cells["T4"] -Formula '=COUNTA(VMData[Name])' + Set-ExcelRange -Address $sheet.Cells["T5"] -Formula '=COUNTIF(VMData[Disk Format], "<>Thin")' + Set-ExcelRange -Address $sheet.Cells["T6"] -Formula '=COUNTIF(VMData[Disk Format], "O")' + Set-ExcelRange -Address $sheet.Cells["T7"] -Formula '=COUNTIF(VMData[Migrating],"X")' + Set-ExcelRange -Address $sheet.Cells["T8"] -Formula '=COUNTIF(VMData[In Prog], "X")' + Set-ExcelRange -Address $sheet.Cells["T9"] -Formula '=COUNTIF(VMData[Complete], "X")' + Set-ExcelRange -Address $sheet.Cells["T10"] -Formula '=T4-T9' + $formula = '=SUM(H3:H' + (2+$vmcount) +')' + Set-ExcelRange -Address $sheet.Cells["T12"] -Formula $formula + Set-ExcelRange -Address $sheet.Cells["T12"] -NFormat '#,##0.00 " GB"' + Set-ExcelRange -Address $sheet.Cells["T13"] -Formula '=SUM(VMData[Used Space Now])' + Set-ExcelRange -Address $sheet.Cells["T13"] -NFormat '#,##0.00 " GB"' + Set-ExcelRange -Address $sheet.Cells["T14"] -Formula '=T12-T13' + Set-ExcelRange -Address $sheet.Cells["T14"] -NFormat '#,##0.00 " GB"' + Set-ExcelRange -Address $sheet.Cells["T15"] -Formula '=IF(T12>0,T14/T12,"")' + Set-ExcelRange -Address $sheet.Cells["T15"] -NFormat '#.00%' + $sheet.Column(8).Hidden = $true + $sheet.Column(20).Width = 14 +} catch { + Write-Host + Write-Host "ERROR! Unable to create file $filename" -ForegroundColor Yellow + Write-Host "Error: $($_.Exception.Message)" -ForegroundColor Red -BackgroundColor Yellow + try { + Close-ExcelPackage $exceltable + Remove-Item $filename + Remove-Item $filename + } catch { + Remove-Item $filename + Remove-Item $filename + } +} + +$exceltable.Workbook.WorkSheets[1].Tables["VMData"].ShowFilter = $false +Close-ExcelPackage $exceltable +Disconnect-V +Write-Host " Success" -ForegroundColor Green + diff --git a/vCenter Tools/HostMultiPingTest.ps1 b/vCenter Tools/HostMultiPingTest.ps1 new file mode 100644 index 0000000..fc4bbe3 --- /dev/null +++ b/vCenter Tools/HostMultiPingTest.ps1 @@ -0,0 +1,317 @@ +param ( + [string]$vCenter = "", + [string]$VMHost = "", + [string]$Test1Vmk = "", + [string]$Test1Host = "", + [int]$Test1Frame = 0, + [string]$Test2Vmk = "", + [string]$Test2Host = "", + [int]$Test2Frame = 0, + [int]$Count = 0, + [switch]$SaveCred = $false, + [switch]$Help = $false +) + +<# Write-Host "vCenter - $vCenter" +Write-Host "VMHost - $VMHost" +Write-Host "Test1VMK - $Test1Vmk" +Write-Host "Test1Host - $Test1Host" +Write-Host "Test1Frame - $Test1Frame" +Write-Host "Test2VMK - $Test2Vmk" +Write-Host "Test2Host - $Test2Host" +Write-Host "Test2Frame - $Test2Frame" +Write-Host "Count - $Count" +Write-Host "SaveCred - $SaveCred"#> + +# Variables +$fileVersion = "1.3.2" +$fileDate = "2023-04-16" + +# Display Script Banner +Write-Host "--------------------------------------------------" -ForegroundColor White +Write-Host "Host Mulitple Concurrent VMK Ping Script" -ForegroundColor Green +Write-Host "Version $($FileVersion) ($($FileDate))" -ForegroundColor Green +Write-Host "Written by Charlie Silverman" -ForegroundColor Green +Write-Host "--------------------------------------------------" -ForegroundColor White + +if ($Help) { + Write-Host + Write-Host "Purpose: Perform multiple ping tests simultaneously from an ESXi host." + Write-Host "Command Syntax:" + Write-Host " HostMultiPingTest.ps1 (-vcenter [vcenter host name]) (-VMHost ""[host]"") (-Test1Host ""[ipaddress]"")" -foregroundcolor green + Write-Host " (-Test1VMK ""[vmkPort]"") (-Test1Frame [framesize]) (-Test2Host ""[ipaddress]"") (-Test2VMK ""[vmkPort]"")" -foregroundcolor green + Write-Host " (-Test2Frame [framesize]) (-count [count]) (-savecred) (-help)`n" -foregroundcolor green + Write-Host "Command Line Options:" + Write-Host + Write-Host -NoNewLine " -vCenter " -foregroundcolor Green + Write-Host "- The FQDN or IP address of a vCenter server." + Write-Host -NoNewLine " -VMHost " -foregroundcolor Green + Write-Host "- The name of the host as shown in vCenter." + Write-Host -NoNewLine " -Test1Host " -foregroundcolor Green + Write-Host "- The IP address of the system to ping for test 1." + Write-Host -NoNewLine " -Test1Vmk " -foregroundcolor Green + Write-Host "- The VMkernel port on the host to ping for test 1." + Write-Host -NoNewLine " -Test1Frame " -foregroundcolor Green + Write-Host "- The TCP frame size to use for test 1. Default: 1500." + Write-Host -NoNewLine " -Test2Host " -foregroundcolor Green + Write-Host "- The IP address of the system to ping for test 2." + Write-Host -NoNewLine " -Test2Vmk " -foregroundcolor Green + Write-Host "- The VMkernel port on the host to ping for test 2." + Write-Host -NoNewLine " -Test2Frame " -foregroundcolor Green + Write-Host "- The TCP frame size to use for test 2. Default: 1500." + Write-Host -NoNewLine " -count " -foregroundcolor Green + Write-Host "- The number of ping tests to run. Default: unlimited." + Write-Host -NoNewLine " -savecred " -foregroundcolor Green + Write-Host "- Configure and store vCenter server credentials. This will only store the credentials under" + Write-Host " the current logged in user." + Write-Host -NoNewLine " -help " -foregroundcolor Green + Write-Host "- Display command help." + Write-Host + Write-Host "If no any of the information is not provided, you will be prompted. If a credential has been previously stored, it will be used again" + Write-Host "unless the -savecred option is specified." + Write-Host + exit + +} + +$quiet = Set-PowerCLIConfiguration -InvalidCertificateAction Ignore -Confirm:$false + +if ($vCenter -eq "") { + $vCenter = Read-Host "Enter name of vCenter Server" +} + +if ($SaveCred) { + $credential = Get-Credential -Message "Enter credentials for vCenter server $vCenter x" + $credential | Export-Clixml -path $env:USERPROFILE\kispingtest-$vcenter.cred + Write-Host "Saving credentials." +} + +if (Test-Path -path $env:USERPROFILE\kispingtest-$vcenter.cred -PathType Leaf) { + $credentials = Import-Clixml -path $env:USERPROFILE\kispingtest-$vcenter.cred + Write-Host "Using stored credentials" +} else { + $credentials = Get-Credential -Message "Enter credentials for vCenter server $vCenter" + if (!$credentials) { + Write-Host "ERROR: No credentials were entered" -foregroundcolor yellow + } +} + +try { + Write-Host -NoNewLine "Connecting to vCenter $vcenter..." + $vcconn = Connect-VIServer $vcenter -Credential $credentials -ErrorAction SilentlyContinue + Write-Host " Success" -ForegroundColor green +} catch { + Write-Host " Failure" -ForegroundColor Yellow -BackgroundColor Red + exit +} + +if ($VMHost -eq "") { + $VMHostlist = Get-VMHost | Sort-Object + While (($VMHost -eq "") -or (-not $VMHost)) { + Write-Host "`nHost Selection:`n" + for ($x = 0; $x -lt $VMHostlist.length; $x++) { + Write-Host "$(([string]($x+1)).PadLeft(3,' '))" -NoNewLine -ForegroundColor Green + Write-Host ": " -NoNewLine -ForegroundColor Gray + Write-Host "$($VMHostlist[$x].name)" -ForegroundColor Yellow + } + $VMHostnum = Read-Host "`nSelect Host Number" + $VMHostnum = [int]$VMHostnum - 1 + $VMHost = $VMHostlist[$VMHostnum] + } + $myhost = get-vmhost $VMHost +} else { + $myhost = get-vmhost $VMHost +} + +$vmks = $myhost | Get-VMHostNetworkAdapter | Where-Object {$_.Name -like "vmk*"} | Sort-Object Name +While (($Test1Vmk -eq "") -or (-not $Test1Vmk)) { + Write-Host "`nVMKernel Port 1 Selection:`n" + for ($x = 0; $x -lt $vmks.length; $x++) { + $myvmk1 = $vmks[$x]; + $mask = [ipaddress]$myvmk1.SubnetMask + $binary = [convert]::ToString($mask.Address, 2) + $mask_length = ($binary -replace 0,$null).Length + Write-Host "$(([string]($x+1)).PadLeft(3,' '))" -NoNewLine -ForegroundColor Green + Write-Host ": " -NoNewLine -ForegroundColor Gray + Write-Host "$($myvmk1.Name) ($($myvmk1.IP)/$($mask_length))" -ForegroundColor Yellow + } + $vmknum = Read-Host "`nSelect VMKernel Port Number for Test 1" + $Test1Vmk = $vmks[$vmknum - 1].Name + $myvmk1 = $vmks[$vmknum - 1] +} + +if ((-not(Test-Path variable:myvmk1) -or ($myvmk1 -ne $null))) { + $myvmk1 = $myhost | Get-VMHostNetworkAdapter | Where-Object {$_.Name -like "$Test1Vmk"} + $mask = [ipaddress]$myvmk1.SubnetMask + $binary = [convert]::ToString($mask.Address, 2) + $mask_length = ($binary -replace 0,$null).Length + Write-Host "Using $($myvmk1.Name) - $($myvmk.PortGroupName) ($($myvmk1.IP)/$($mask_length)) for Test 1" +} + +$vmk1testvalid = $false +while (-not $vmk1testvalid) { + if ($Test1Host -ne "") { + $thisip = [ipaddress]$Test1Host + $subnet = [ipaddress]$myvmk1.IP + $netmask = [ipaddress]$myvmk1.SubnetMask + $ipbits = [int[]]$($myvmk1.IP).split('.') + $maskbits = [int[]]$($myvmk1.SubnetMask).split('.') + $networkidbits = 0..3 | ForEach-Object { $ipbits[$_] -band $maskbits[$_] } + $networkid = [ipaddress]($networkidbits -join '.') + If ($networkid.Address -eq ($thisip.Address -band $netmask.Address)) { + $vmk1testvalid = $true + } else { + Write-Host "Error: IP Address is outside the allowed subnet." -ForegroundColor Red + $Test1Host = Read-Host "Enter IP Address to ping for Test 1" + } + } else { + $Test1Host = Read-Host "Enter IP Address to ping for Test 1" + } +} + +While ( (-not ($Test1Frame -is [int]) -or ($Test1Frame -lt 1500) ) ) { + if ($Test1Frame -eq 0) { + $Test1Frame = Read-Host "`nFrame size for Test 1 (1500)" + if (!($Test1Frame -is [int]) -or ($Test1Frame -lt 1500)) { $Test1Frame = 1500 } + } +} + +While (($Test2Vmk -eq "") -or (-not $Test2Vmk)) { + Write-Host "`nVMKernel Port 2 Selection:`n" + for ($x = 0; $x -lt $vmks.length; $x++) { + $myvmk2 = $vmks[$x] + if ($myvmk2.Name.substring(0,3) -ne "vmk") { + continue + } + $mask = [ipaddress]$myvmk2.SubnetMask + $binary = [convert]::ToString($mask.Address, 2) + $mask_length = ($binary -replace 0,$null).Length + Write-Host "$(([string]($x+1)).PadLeft(3,' '))" -NoNewLine -ForegroundColor Green + Write-Host ": " -NoNewLine -ForegroundColor Gray + Write-Host "$($myvmk2.Name) - $($myvmk.PortGroupName) ($($myvmk2.IP)/$($mask_length))" -ForegroundColor Yellow + } + $vmknum = Read-Host "`nSelect VMKernel Port Number for Test 2" + $Test2Vmk = $vmks[$vmknum - 1].Name + $myvmk2 = $vmks[$vmknum - 1] +} + + +if ((-not(Test-Path variable:myvmk2) -or ($myvmk2 -ne $null))) { + $myvmk2 = $myhost | Get-VMHostNetworkAdapter | Where-Object {$_.Name -like "$Test2Vmk"} + $mask = [ipaddress]$myvmk2.SubnetMask + $binary = [convert]::ToString($mask.Address, 2) + $mask_length = ($binary -replace 0,$null).Length + Write-Host "Using $($myvmk2.Name) ($($myvmk2.IP)/$($mask_length)) for Test 2" +} + +$vmk2testvalid = $false +while (-not $vmk2testvalid) { + if (($Test2Host -ne "") -and $Test2Host) { + $thisip = [ipaddress]$Test2Host + $subnet = [ipaddress]$myvmk2.IP + $netmask = [ipaddress]$myvmk2.SubnetMask + $ipbits = [int[]]$($myvmk2.IP).split('.') + $maskbits = [int[]]$($myvmk2.SubnetMask).split('.') + $networkidbits = 0..3 | ForEach-Object { $ipbits[$_] -band $maskbits[$_] } + $networkid = [ipaddress]($networkidbits -join '.') + If ($networkid.Address -eq ($thisip.Address -band $netmask.Address)) { + $vmk2testvalid = $true + } else { + Write-Host "Error: IP Address is outside the allowed subnet." -ForegroundColor Red + $Test2Host = Read-Host "Enter IP Address to ping for Test 2" + } + } else { + $Test2Host = Read-Host "Enter IP Address to ping for Test 2" + } +} + +While ( (-not ($Test2Frame -is [int]) -or ($Test2Frame -lt 1500) ) ) { + if ($Test2Frame -eq 0) { + $Test2Frame = Read-Host "`nFrame size for Test 2 (1500)" + if (!($Test2Frame -is [int]) -or ($Test2Frame -lt 1500)) { $Test2Frame = 1500 } + } +} + +Write-host "`nBegin Testing:" + +$esxcli = get-esxcli -vmhost (get-vmhost $myhost) -v2 +$params1 = (get-esxcli -v2 -vmhost (get-vmhost $myhost)).network.diag.ping.CreateArgs() +$params2 = (get-esxcli -v2 -vmhost (get-vmhost $myhost)).network.diag.ping.CreateArgs() +$params1.host = $Test1Host +$params1.interface = $Test1Vmk +$params1.size = $Test1Frame +$params1.count = 1 +$params2.host = $Test2Host +$params2.interface = $Test2Vmk +$params2.size = $Test2Frame +$params2.count = 1 +$counter = 0 +$test1passcount = 0 +$test2passcount = 0 +try { + while (($counter -lt $Count) -or ($Count -eq 0)) { + $counter += 1 + $result1 = ($esxcli.network.diag.ping.Invoke($params1)).summary + $result2 = ($esxcli.network.diag.ping.Invoke($params2)).summary + $part1 = $Test1Vmk + "->" + $result1.HostAddr + "(" + $Test1Frame + ") " + if ($result1.Received -eq 1) { + $test1 = "Good" + ++$test1passcount + } else { + $test1 = "Bad" + } + $part2 = $Test2Vmk + "->" + $result2.HostAddr + "(" + $Test2Frame + ") " + if ($result2.Received -eq 1) { + $test2 = "Good" + ++$test2passcount + } else { + $test2 = "Bad" + } + Write-Host "$(([string]$counter).PadLeft(4,' ')): $($VMHost): $part1" -NoNewLine + If ($test1 -eq "Good") { + Write-Host "$test1" -NoNewLine -ForegroundColor Green + } else { + Write-Host "$test1" -NoNewLine -ForegroundColor Red + } + Write-Host " $part2" -NoNewLine + If ($test2 -eq "Good") { + Write-Host "$test2" -ForegroundColor Green + } else { + Write-Host "$test2" -ForegroundColor Red + } + Start-Sleep -seconds 1 + } +} finally { + if ($counter -ne $Count) { + Write-Host "`n--- Script processing cancelled." -ForegroundColor Red + } + Write-Host "`n------------------------------------------------" + Write-Host "Results:" + Write-Host "From: " -NoNewLine + Write-Host $VMHost -ForegroundColor Yellow + Write-Host "Count: " -NoNewLine + Write-Host $counter -ForegroundColor Yellow + Write-Host "`nTest 1:" + Write-Host "VMKernel port: " -NoNewLine + Write-Host $Test1Vmk -ForegroundColor Yellow + Write-Host "Destination: " -NoNewLine + Write-Host $Test1Host -ForegroundColor Yellow + Write-Host "Frame Size: " -NoNewLine + Write-Host $Test1Frame -ForegroundColor Yellow + Write-Host "Success: " -NoNewLine + Write-Host $test1passcount -ForegroundColor Green -NoNewLine + Write-Host " - Failure: " -NoNewLine + Write-Host ($counter - $test1passcount) -ForegroundColor Red + Write-Host "`nTest 2:" + Write-Host "VMKernel port: " -NoNewLine + Write-Host $Test2Vmk -ForegroundColor Yellow + Write-Host "Destination: " -NoNewLine + Write-Host $Test2Host -ForegroundColor Yellow + Write-Host "Frame Size: " -NoNewLine + Write-Host $Test2Frame -ForegroundColor Yellow + Write-Host "Success: " -NoNewLine + Write-Host $test2passcount -ForegroundColor Green -NoNewLine + Write-Host " - Failure: " -NoNewLine + Write-Host ($counter - $test2passcount) -ForegroundColor Red + +} \ No newline at end of file diff --git a/vCenter Tools/NetworkTester.ps1 b/vCenter Tools/NetworkTester.ps1 new file mode 100644 index 0000000..a20db04 --- /dev/null +++ b/vCenter Tools/NetworkTester.ps1 @@ -0,0 +1,183 @@ +param ( + [string]$vCenter = "", + [string]$cluster = "", + [string]$VmHost = "", + [string]$vmkPort = "", + [int]$testFrame = 1472, + [switch]$SaveCred = $false, + [switch]$Help = $false +) + +# Variables +$fileVersion = "1.2.6" +$fileDate = "2025-09-15" + +# Display Script Banner +Write-Host "--------------------------------------------------" -ForegroundColor White +Write-Host "Host Network Testing Script" -ForegroundColor Green +Write-Host "Version $($FileVersion) ($($FileDate))" -ForegroundColor Green +Write-Host "Written by Charlie Silverman" -ForegroundColor Green +Write-Host "--------------------------------------------------" -ForegroundColor White + +if ($Help) { + Write-Host + Write-Host "Purpose: Perform multiple ping tests simultaneously from an ESXi host." + Write-Host "Command Syntax:" + Write-Host " NetworkTester.ps1 (-vcenter [vcenter host name]) (-cluster ""[cluster]"") (-VmHost ""[host]"") (-vmkPort ""[vmkPort]"")" -foregroundcolor green + Write-Host " (-testFrame [framesize]) (-savecred) (-help)`n" -foregroundcolor green + Write-Host "Command Line Options:" + Write-Host + Write-Host -NoNewLine " -vCenter " -foregroundcolor Green + Write-Host "- The FQDN or IP address of a vCenter server." + Write-Host -NoNewLine " -cluster " -foregroundcolor Green + Write-Host "- The name of the cluster as shown in vCenter." + Write-Host -NoNewLine " -VmHost " -foregroundcolor Green + Write-Host "- [Optional] The name of a host to test. If not specified, all hosts in the cluster will be tested." + Write-Host -NoNewLine " -vmkPort " -foregroundcolor Green + Write-Host "- The VMkernel port on the host to test." + Write-Host -NoNewLine " -testFrame " -foregroundcolor Green + Write-Host "- The TCP frame size to use for the test. Default: 1500." + Write-Host -NoNewLine " -savecred " -foregroundcolor Green + Write-Host "- Configure and store vCenter server credentials. This will only store the credentials under" + Write-Host " the current logged in user." + Write-Host -NoNewLine " -help " -foregroundcolor Green + Write-Host "- Display command help." + Write-Host + Write-Host "If any of the information is not provided, you will be prompted. If a credential has been previously stored, it will" + Write-Host "be used again unless the -savecred option is specified." + Write-Host + Write-Host "Each host in the selected cluster will perform ping tests to all IP addresses assigned to the selected vmKernel port" + Write-Host "on all hosts in the cluster." + Write-Host + exit +} + +$quiet = Set-PowerCLIConfiguration -InvalidCertificateAction Ignore -Confirm:$false + +if ($vCenter -eq "") { + $vCenter = Read-Host "Enter name of vCenter Server" +} + +if ($SaveCred) { + $credential = Get-Credential -Message "Enter credentials for vCenter server $vCenter" + $credential | Export-Clixml -path $env:USERPROFILE\kispingtest-$vcenter.cred + Write-Host "Saving credentials." +} + +if (Test-Path -path $env:USERPROFILE\kispingtest-$vcenter.cred -PathType Leaf) { + $credentials = Import-Clixml -path $env:USERPROFILE\kispingtest-$vcenter.cred + Write-Host "Using stored credentials" +} else { + $credentials = Get-Credential -Message "Enter credentials for vCenter server $vCenter" + if (!$credentials) { + Write-Host "ERROR: No credentials were entered" -foregroundcolor yellow + } +} + +try { + Write-Host -NoNewLine "Connecting to vCenter $vcenter..." + $vcconn = Connect-VIServer $vcenter -Credential $credentials -ErrorAction SilentlyContinue + Write-Host " Success" -ForegroundColor green +} catch { + Write-Host " Failure" -ForegroundColor Yellow -BackgroundColor Red + exit +} + +if ($cluster -eq "") { + $Clusterlist = Get-Cluster | Sort-Object + While (($cluster -eq "") -or (-not $cluster)) { + Write-Host "`nCluster Selection:`n" + for ($x = 0; $x -lt $Clusterlist.length; $x++) { + Write-Host "$(([string]($x+1)).PadLeft(3,' '))" -NoNewLine -ForegroundColor Green + Write-Host ": " -NoNewLine -ForegroundColor Gray + Write-Host "$($Clusterlist[$x].name)" -ForegroundColor Yellow + } + $Clusternum = Read-Host "`nSelect Cluster Number" + $Clusternum = [int]$Clusternum - 1 + $cluster = $Clusterlist[$Clusternum] + } + $mycluster = get-cluster $cluster +} else { + $mycluster = get-cluster $cluster +} + +$firsthost = $mycluster | Get-VMHost | Select-Object -first 1 + +$vmks = $firsthost | Get-VMHostNetworkAdapter | Where-Object {$_.Name -like "vmk*"} | Sort-Object Name +While (($vmkPort -eq "") -or (-not $vmkPort)) { + Write-Host "`nVMKernel Port Selection:`n" + for ($x = 0; $x -lt $vmks.length; $x++) { + $myvmk = $vmks[$x]; + $mask = [ipaddress]$myvmk.SubnetMask + $binary = [convert]::ToString($mask.Address, 2) + $mask_length = ($binary -replace 0,$null).Length + Write-Host "$(([string]($x+1)).PadLeft(3,' '))" -NoNewLine -ForegroundColor Green + Write-Host ": " -NoNewLine -ForegroundColor Gray + Write-Host "$($myvmk.Name) - $($myvmk.PortGroupName) ($($myvmk.IP)/$($mask_length))" -ForegroundColor Yellow + } + $vmknum = Read-Host "`nSelect VMKernel Port Number for Testing" + $vmkPort = $vmks[$vmknum - 1].Name + $myvmk = $vmks[$vmknum - 1] +} + +if ((-not(Test-Path variable:myvmk) -or ($myvmk -ne $null))) { + $myvmk = $firsthost | Get-VMHostNetworkAdapter | Where-Object {$_.Name -like "$vmkPort"} + $mask = [ipaddress]$myvmk.SubnetMask + $binary = [convert]::ToString($mask.Address, 2) + $mask_length = ($binary -replace 0,$null).Length + Write-Host "Using $($myvmk.Name) ($($myvmk.PortGroupName)) for Testing" +} + +$targetlist = get-cluster $mycluster | get-vmhost | Get-VMHostNetworkAdapter | Where-Object {$_.Name -like "$myvmk"} | Select-Object -Property IP | Sort-Object IP + +if ($VmHost -ne "") { + try { + $hostlist = Get-VMhost $VmHost -ErrorAction Stop + } catch { + Write-Host "ERROR - Host $VmHost not found." -ForegroundColor Yellow -BackgroundColor Red + exit* + } + if ($hostlist[0].parent.name -ne $cluster) { + Write-Host "ERROR - Host $VmHost not part of cluster $cluster" -ForegroundColor Yellow -BackgroundColor Red + exit + } +} else { + $hostlist = $mycluster | Get-VMHost | Sort-Object Name +} + +for ($x = 0; $x -lt $hostlist.length; $x++) { + $myhost = $hostlist[$x] + $myip = $myhost | Get-VMHostNetworkAdapter | Where-Object {$_.Name -like "$myvmk"} | Select-Object -Property IP + $hostname = $myhost.name + $tcpstack = $myvmk.ExtensionData.Spec.NetStackInstanceKey + Write-Host "Testing Host $hostname on $($myvmk.PortGroupName) with a frame size of $testFrame ($myip)" + $esxcli = get-esxcli -vmhost (get-vmhost $myhost) -v2 + for ($y = 0; $y -lt $targetlist.length; $y++) { + $params = (get-esxcli -v2 -vmhost (get-vmhost $myhost)).network.diag.ping.CreateArgs() + $params.host = $targetlist[$y].IP + $params.interface = $vmkPort + $params.size = $testFrame + $params.df = $true + $params.count = 1 + if ($tcpstack -ne "defaultTcpipStack") { + $params.netstack = $tcpstack + } + $result = ($esxcli.network.diag.ping.Invoke($params)).summary + $response = $vmkPort + "->" + $result.HostAddr + "(" + $testFrame + ") " + if ($result.Received -gt 0) { + $test = "Good" + } else { + $test = "Bad" + } + + Write-Host " - $($hostname): $response" -NoNewLine + If ($test -eq "Good") { + Write-Host "$test" -ForegroundColor Green + } else { + Write-Host "$test" -ForegroundColor Red + } + } +} + +Disconnect-VIServer $vCenter -confirm:$false + diff --git a/vCenter Tools/RunESXiCommand.ps1 b/vCenter Tools/RunESXiCommand.ps1 new file mode 100644 index 0000000..89f2e88 --- /dev/null +++ b/vCenter Tools/RunESXiCommand.ps1 @@ -0,0 +1,142 @@ +#requires -Modules posh-ssh + +param ( + [string]$vCenter = "", + [string]$Cluster = "", + [string]$Command = "", + [switch]$SaveCred = $false, + [switch]$Help = $false +) + +# Variables +$FileVersion = "1.0.1" +$FileDate = "2026-04-18" + +# Display Script Banner +Write-Host "--------------------------------------------------" -ForegroundColor White +Write-Host "Run ESXi CLI Command" -ForegroundColor Green +Write-Host "Version $($FileVersion) ($($FileDate))" -ForegroundColor Green +Write-Host "Written by Charlie Silverman" -ForegroundColor Green +Write-Host "--------------------------------------------------" -ForegroundColor White + +if ($Help) { + Write-Host + Write-Host "Purpose: Run a CLI command on all hosts in a cluster." + Write-Host "Command Syntax:" + Write-Host " RunESXiCommand.ps1 (-vCenter [vcenter host name]) (-Cluster ""[cluster]"") (-Command ""[command]"") (-savecred)" -foregroundcolor green + Write-Host " (-NovCenter) (-savecred) (-help)`n" -foregroundcolor green + Write-Host "Command Line Options:" + Write-Host + Write-Host -NoNewLine " -vCenter " -foregroundcolor Green + Write-Host "- The FQDN or IP address of a vCenter server." + Write-Host -NoNewLine " -Cluster " -foregroundcolor Green + Write-Host "- The name of the cluster as shown in vCenter." + Write-Host -NoNewLine " -Command " -foregroundcolor Green + Write-Host "- The full CLI command to run on each host." + Write-Host -NoNewLine " -Savecred " -foregroundcolor Green + Write-Host "- Configure and store vCenter server credentials. This will only store the credentials under" + Write-Host " the current logged in user." + Write-Host -NoNewLine " -help " -foregroundcolor Green + Write-Host "- Display command help." + Write-Host + Write-Host "If any of the information is not provided, you will be prompted. If a credential has been previously stored, it will" + Write-Host "be used again unless the -savecred option is specified." + exit +} + +$quiet = Set-PowerCLIConfiguration -InvalidCertificateAction Ignore -Confirm:$false + +if ($vCenter -eq "") { + $vCenter = Read-Host "Enter name of vCenter Server" +} + +if ($SaveCred) { + $credential = Get-Credential -Message "Enter credentials for vCenter server $vCenter" + $credential | Export-Clixml -path $env:USERPROFILE\kistools-$vcenter.cred + Write-Host "Saving vCenter credentials." + $credential = Get-Credential -Message "Enter credentials for ESXi hosts in $vCenter" + $credential | Export-Clixml -path $env:USERPROFILE\kistools-$vcenter-esxi.cred + Write-Host "Saving ESXi credentials." +} + +if (Test-Path -path $env:USERPROFILE\kistools-$vcenter.cred -PathType Leaf) { + $credentials = Import-Clixml -path $env:USERPROFILE\kistools-$vcenter.cred + Write-Host "Using stored vCenter credentials" +} else { + $credentials = Get-Credential -Message "Enter credentials for vCenter server $vCenter" + if (!$credentials) { + Write-Host "ERROR: No credentials were entered" -foregroundcolor yellow + } +} + +if (Test-Path -path $env:USERPROFILE\kistools-$vcenter-esxi.cred -PathType Leaf) { + $credentials_ssh = Import-Clixml -path $env:USERPROFILE\kistools-$vcenter-esxi.cred + Write-Host "Using stored ESXi credentials" +} else { + $credentials_ssh = Get-Credential -Message "Enter credentials for ESXi hosts in $vCenter" + if (!$credentials) { + Write-Host "ERROR: No credentials were entered" -foregroundcolor yellow + } +} + +try { + Write-Host -NoNewLine "Connecting to vCenter $vcenter..." + $vcconn = Connect-VIServer $vcenter -Credential $credentials -ErrorAction SilentlyContinue + $test = get-cluster + Write-Host " Success" -ForegroundColor green +} catch { + Write-Host " Failure" -ForegroundColor Yellow -BackgroundColor Red + Write-Host "Error: $($_.Exception.Message)" + exit +} + +if ($cluster -eq "") { + $Clusterlist = Get-Cluster | Sort-Object + While (($cluster -eq "") -or (-not $cluster)) { + Write-Host "`nCluster Selection:`n" + for ($x = 0; $x -lt $Clusterlist.length; $x++) { + Write-Host "$(([string]($x+1)).PadLeft(3,' '))" -NoNewLine -ForegroundColor Green + Write-Host ": " -NoNewLine -ForegroundColor Gray + Write-Host "$($Clusterlist[$x].name)" -ForegroundColor Yellow + } + $Clusternum = Read-Host "`nSelect Cluster Number" + $Clusternum = [int]$Clusternum - 1 + $cluster = $Clusterlist[$Clusternum] + } + $mycluster = get-cluster $cluster +} else { + $mycluster = get-cluster $cluster +} + +if ($Command -eq "") { + $Command = Read-Host "Enter CLI Command to run on each host" +} + +$myhosts = $mycluster | Get-VMHost | Sort-Object -Property Name + +foreach($myhost in $myhosts) { + $myssh = $myhost | Get-VMHostService | Where-Object {$_.Key -eq "TSM-SSH"} + if (!$myssh.Running) { + Write-Host "-- Starting SSH Service on Host $($myhost.Name)" + $quiet = $myssh | Start-VMHostService -Confirm:$false + } + + $sshsession = New-SSHSession -ComputerName $myhost.Name -Credential $credentials_ssh -AcceptKey -KeepAliveInterval 5 + Write-Host "-- Running command " -NoNewLine + Write-Host "$Command" -NoNewLine -ForegroundColor yellow + Write-Host " on host " -NoNewLine + Write-Host "$myhost" -ForegroundColor yellow + $result = Invoke-SSHCommand -SessionID $sshsession.SessionId -Command $Command -Timeout 30 + Write-Host "Command Output:" + $result.Output + Write-Host "Exit Status: $($result.ExitStatus)" + $quiet = Remove-SSHSession -SessionID $sshsession.SessionId + + if (!$myssh.Running) { + Write-Host "-- Stoppnig SSH Service on Host $($myhost.Name)" + $quiet = $myssh | Stop-VMHostService -Confirm:$false + } + Write-Host "----------------------------" +} + +