Tag Archives: powershell

Get-MailboxDatabase doesn’t show last backup timestamp

This posting is ~7 years years old. You should keep this in mind. IT is a short living business. This information might be outdated.

Sometimes you have to check when the last backup of an Exchange mailbox database was taken. This is pretty simple, because the timestamps of the last full, incremental and differential backup is stored for each mailbox database. You can check these attributes using the Exchange Control Panel (ECP), or you can use the Get-MailboxDatabase cmdlet.

Backup successful, but no timestamp?

Take a look at this output. As you can see, there’s no timestamp for the last full, incremental and differential backup. But this database was successfully backuped some minutes before.

[PS] C:\Windows\system32>get-mailboxdatabase | select name, *backup*


Name                           : DB01
BackupInProgress               :
SnapshotLastFullBackup         :
SnapshotLastIncrementalBackup  :
SnapshotLastDifferentialBackup :
SnapshotLastCopyBackup         :
LastFullBackup                 :
LastIncrementalBackup          :
LastDifferentialBackup         :
LastCopyBackup                 :
RetainDeletedItemsUntilBackup  : False

Missing -status switch

The solution is easy: The -status  switch was missing.

[PS] C:\Windows\system32>Get-MailboxDatabase -status | fl name, *backup*


Name                           : DB01
BackupInProgress               : True
SnapshotLastFullBackup         : True
SnapshotLastIncrementalBackup  :
SnapshotLastDifferentialBackup :
SnapshotLastCopyBackup         :
LastFullBackup                 : 19.07.2016 15:22:24
LastIncrementalBackup          :
LastDifferentialBackup         :
LastCopyBackup                 :
RetainDeletedItemsUntilBackup  : False

[PS] C:\Windows\system32>Get-MailboxDatabase -status | fl name, *backup*


Name                           : DB01
BackupInProgress               : False
SnapshotLastFullBackup         : True
SnapshotLastIncrementalBackup  :
SnapshotLastDifferentialBackup :
SnapshotLastCopyBackup         :
LastFullBackup                 : 16.08.2016 15:04:02
LastIncrementalBackup          :
LastDifferentialBackup         :
LastCopyBackup                 :
RetainDeletedItemsUntilBackup  : False

After adding the -status  switch to the Get-MailboxDatabase command, the timestamps were added to the output. If you run the command during a running backup session, this is also added to the output (BackupInProgress).

Exchange Management Shell (EMS) and new PowerShell releases

This posting is ~7 years years old. You should keep this in mind. IT is a short living business. This information might be outdated.

Some day ago, I installed a new Exchange 2013 CU11 for some test ins my lab. Nothing fancy, just a single server deployment on a Windows Server 2012 R2 VM. I deployed this Windows Server from a template, which was updated with the latest Windows Patches and WMF some days ago. The Exchange setup went smooth. I updated the SSL certificates and the internal and external URLs for the virtual directories. Then I started the Exchange Management Shell (EMS), to update the Autodiscover URL in the service connection point (SCP) of the Active Directory.

VERBOSE: Connecting to exchange1.lab.local.
New-PSSession : Cannot find path '' because it does not exist.
At line:1 char:1
+ New-PSSession -ConnectionURI "$connectionUri" -ConfigurationName Micr ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : OpenError: (System.Manageme....RemoteRunspace:RemoteRunspace) [New-PSSession], RemoteException
    + FullyQualifiedErrorId : PSSessionOpenFailed

Well… that doesn’t look successful. I quickly switched to a PowerShell windows and imported the Exchange snap-in manually.

Windows PowerShell
Copyright (C) 2015 Microsoft Corporation. All rights reserved.

PS C:\Users\Administrator> Add-PSSnapin Microsoft.Exchange.Management.PowerShell.E2010
PS C:\Users\Administrator> & 'C:\Program Files\Microsoft\Exchange Server\V15\Bin\RemoteExchange.ps1'

         Welcome to the Exchange Management Shell!

Full list of cmdlets: Get-Command
Only Exchange cmdlets: Get-ExCommand
Cmdlets that match a specific string: Help *<string>*
Get general help: Help
Get help for a cmdlet: Help <cmdlet name> or <cmdlet name> -?
Exchange team blog: Get-ExBlog
Show full output for a command: <command> | Format-List

Show quick reference guide: QuickRef
Tip of the day #22:

Get all Win32 WMI information, such as Perfmon counters and local computer configurations. For example, type:

 Get-WMIObject Win32_PerfRawData_PerfOS_Memory

PS C:\Users\Administrator> Get-ExchangeServer | ft -AutoSize

Name      Site                                  ServerRole            Edition    AdminDisplayVersion
----      ----                                  ----------            -------    -------------------
EXCHANGE1 lab.local/Configuration/Sites/Cologne Mailbox, ClientAccess Enterprise Version 15.0 (Build 1130.7)


PS C:\Users\Administrator>

Looks better, isn’t it?

I compared my lab setup to a running Exchange 2013 single server deployment and I stumbled over the PowerShell version. In addition, I found the Windows Management Framework 5 Production Preview (KB3066437) on my freshly deployed Windows Server 2012 R2 VM.

[PS] C:\Windows\system32>$PSVersionTable.PSVersion

Major  Minor  Build  Revision
-----  -----  -----  --------
5      0      10514  6

After checking the Exchange Server Supportability Matrix, it was clear what had happened: WMF 5 is not supported (Source). Not supported with Exchange 2013, and also not supported with Exchange 2016.

exchange_supported_wmf

After I had removed KB3066437 from my Exchange server, the EMS loaded successfully.

         Welcome to the Exchange Management Shell!

Full list of cmdlets: Get-Command
Only Exchange cmdlets: Get-ExCommand
Cmdlets that match a specific string: Help *<string>*
Get general help: Help
Get help for a cmdlet: Help <cmdlet name> or <cmdlet name> -?
Exchange team blog: Get-ExBlog
Show full output for a command: <command> | Format-List

Show quick reference guide: QuickRef
Tip of the day #25:

One benefit of the Exchange Management Shell is that cmdlets can output objects to the console. You can then manipulate this output and organize it in interesting wa
ys. For example, to get a quick view in tabular format, use Format-Table:

 Get-Mailbox | Format-Table Name,Database,RulesQuota

VERBOSE: Connecting to exchange1.lab.local.
VERBOSE: Connected to exchange1.lab.local.
[PS] C:\Windows\system32>

You should ALWAYS check if installed applications are supported with newer version of PowerShell/ WMF! Currentyl, no Exchange version is supported with PowerShell 5/ WMF 5.

PowerShell ISE on steroids

This posting is ~7 years years old. You should keep this in mind. IT is a short living business. This information might be outdated.

I’m not a developer. I deal mainly with infrastructe, things like virtualization, storage & backup, networking etc. Sometimes I had to write scripts, primarily PowerShell, batch or Bash. Many years back, I also wrote Csh and Ksh scripts. In the past years, automation was one of the rising trends in the infrastructure segment. And with automation, new challenges came up. Today I have to work with Windows PowerShell, in case of VMware with PowerCLI (which bases on Windows PowerShell), and sometimes I have use with REST APIs. I’m still not a developer. Due to this fact, I need tools that help me getting my work done.

So I was searching for a tool, mainly for PowerShell development, and I’ve tried some tools. Microsoft Visual Studio was to complex. Microsoft Visual Studio Code was light, but offered not the features I needed. The Windows integrated PowerShell ISE was nice, but it also lacked some features. So I asked on Twitter:

The answer was simple: ISESteroids.

What is ISESteroids?

ISESteroids is not a standalone product. It’s a PowerShell module that extents the built-in PowerShell ISE. That’s nice, because you don’t have to install anything. Simply extract it. You don’t need any special privileges to install it. Load the PowerShell module, done.

ISESteroids offers a broad feature set and transforms the PowerShell ISE into a full-featured PowerShell IDE. Visit the ISESteroids homepage for a full feature list. Nothing I want to copy & paste here.

Why is ISESteroids helpful for me?

As already mentioned: I’m not a developer. Therefore, I’m thankful for all hints and tips to make my scripts better. One of the features that I noticed immediately was the light bulb on the left side of the scripting area. The icon indicates that there is an automatic fix. In my case, this is usually converting double into single quotes.

ISESteroids_01

Patrick Terlisten/ www.vcloudnine.de/ Creative Commons CC0

Another often mentioned fix is the replacement of aliases with the full command names. Another feature I really like is the risk analyzer. Sometimes you use commands and functions, that might not work with future releases, or which involve other risks. The risk analyzer is an easy way to highlight these risky commands and functions.

ISESteroids_02

Patrick Terlisten/ www.vcloudnine.de/ Creative Commons CC0

Green indicates: Everything’s fine. If something risky is found, you will get a explanation why this was marked as a risky element. If you still want to use it, you can add the marked element to a whitelist. Some risks, are not a risk at all. The risk analyzer will mark the usage of the cmdlet Move-VM as a risk. This is because cmdlets with the verb “Move” will move things. IN case of Move-VM, this is intended. That’s something you can certainly whitelists.

ISESteroids_03

Patrick Terlisten/ www.vcloudnine.de/ Creative Commons CC0

One of my most used cmdlets is Get-Help. Intellisense is nice, but sometimes I have to look up the correct syntax or similar. ISESteroids offers a context sensitive help. Click on the icon with the question mark,

ISESteroids_04

Patrick Terlisten/ www.vcloudnine.de/ Creative Commons CC0

and you will see a new add-on tab on the left. Very handy. Click on a command, and the help will appear help add-on tab.

ISESteroids_05

Patrick Terlisten/ www.vcloudnine.de/ Creative Commons CC0

You might notice another add-on tab in the picture above: Variables. This tab belongs to the Variables monitor, which can be useful to watch the content of variables. I use it frequently in conjunction with the debugging function.

ISESteroids_07

Patrick Terlisten/ www.vcloudnine.de/ Creative Commons CC0

You can set breakpoints, add variables to the monitor and then watch the content of the variable.

ISESteroids_08

Patrick Terlisten/ www.vcloudnine.de/ Creative Commons CC0

But you can also take a look at the current content of variables, in this case $VMhostScsiLunPaths.

ISESteroids_09

Patrick Terlisten/ www.vcloudnine.de/ Creative Commons CC0

The last feature I’d like to show, is the AutoRefactor. Usually, I tend to follow best practices (mostly my own…) to make my scripts more “readable”. The AutoRefactor feature of ISESteroids helps me to make my scripts cleaner and more readable. It’s customizable, so I can tweak it where necessary. You can enable the refactor add-on tab by clicking the small icon with the check mark.

ISESteroids_06

Patrick Terlisten/ www.vcloudnine.de/ Creative Commons CC0

Write down the code and click “Fix Script Now”. Then watch the magic. ;)

Why didn’t I highlight the other cool features, like code signing, file version control, keyboard shortcuts or test arguments? Because I’m still not a developer. The features I mentioned in this blog post are worthy enough to buy a PowerShell ISE license. Check the full feature list, download and install the trial version. I really recommend to take a look at the trial version! I was sceptical until I worked with ISESteroids. It was a great recommendation!

Licensing

ISESteroids is available in two commercial licenses:

  • Professional
  • Enterprise

The Professional license is available for 99 €, the Enterprise license costs 249 €. Latter offers more features. For individuals (natural persons), a discounted Enterprise license (99 €) is available. Startups, MVPs, trainers etc. can request a discounted license. Check the order website for more details.

PowerCLI: Get-LunPathState

This posting is ~7 years years old. You should keep this in mind. IT is a short living business. This information might be outdated.

Careful preparation is a key element to success. If you restart a storage controller, or even the whole storage, you should be very sure that all ESXi hosts have enough paths to every datstore. Sure, you can use the VMware vSphere C# client or the Web Client to check every host and every datastore. But if you have a large cluster with a dozen datastores and some Raw Device Mappings (RDMs), this can take a looooong time. Checking the path state of each LUN is a task, which can be perfectly automated. Get a list of all hosts, loop through every host and every LUN, output a list of all hosts with all LUNs and all paths for each LUN. Sounds easy, right?

For a long time, I used this PowerCLI script for checking the LUN path state. But now I decided to give something back and I tweaked it a bit for my needs.

Feel free to use and/ or modify it.

Starting and stopping Azure VMs with Azure PowerShell

This posting is ~7 years years old. You should keep this in mind. IT is a short living business. This information might be outdated.

To be honest: I’m lazy and I have a wife and two kids. Therefore I have to minimize the costs of my lab. I have a physical lab at the office and some VMs running on Microsoft Azure. Azure is nice, because I only have to pay what I really use. And because I’m only paying the actual use, I start the VMs only when I need them. Inspired by this very handy Azure VM wakeup & shutdown script, I decided to write my own script (yes, I invented a wheel again…). Very simple, nothing fancy. Feel free to use and modify the script according to your needs.

Automating ESXi configuration for DataCore SANsymphony-V

This posting is ~8 years years old. You should keep this in mind. IT is a short living business. This information might be outdated.

DataCore describes in their Host Configuration Guide for VMware ESXi some settings that must be adjusted before storage from DataCore SANsymphony-V storage servers will be assigned to the ESXi hosts. Today, for ESXi 5.x and 6.0, you have to add a custom rule and adjust the advanced setting DiskMaxIOSize. For ESX(i) 4 more parameters had to be adjusted. But I will focus on  ESXi 5.x and 6.0. You need to adjust these settings for each host that should get storage mapped from a DataCore storage server. If you have more then one host, you may have the wish to automate the necessary steps. The check the current value of DiskMaxIOSize, you can use this lines of PowerCLI code.

$esxCluster = 'LAB'
$esxHosts = Get-Cluster $esxCluster | Get-VMHost | Where { $_.PowerState -eq "PoweredOn" -and $_.ConnectionState -eq "Connected" } | Sort Name

foreach($esx in $esxHosts) {

Get-AdvancedSetting -Entity $esx -Name Disk.DiskMaxIOSize | Select Entity,Name,Value 

}

So set DiskMaxIOSize to the recommended value of 512 and to create the custom SATP rule, use this lines:

$esxCluster = 'LAB'
$esxHosts = Get-Cluster $esxCluster | Get-VMHost | Where { $_.PowerState -eq "PoweredOn" -and $_.ConnectionState -eq "Connected" } | Sort Name

foreach ($esx in $esxHosts) {

$esxcli = Get-EsxCli -VMHost $esx
$esxcli.storage.nmp.satp.rule.add($null,"tpgs_on","DataCore SANsymphony-V Custom ALUA Rule",$null,$null,$null,"Virtual Disk",$null,"VMW_PSP_RR",$null,"VMW_SATP_ALUA",$null,$null,"DataCore")

Get-AdvancedSetting -Entity $esx -Name Disk.DiskMaxIOSize | Set-AdvancedSetting -Value 512 -Confirm:$false | Out-Null

Write-Host "Finished $esx"

}

Please make sure that you adjust the $esxCluster variable.  Nothing fancy, but it can save some time – even if you only have two hosts. ;)

How to shrink thin-provisioned disks

This posting is ~8 years years old. You should keep this in mind. IT is a short living business. This information might be outdated.

Disk space is rare. I only have about 1 TB of SSD storage in my lab and I don’t like to waste too much of it. My hosts use NFS to connect to my Synology NAS, and even if I use the VAAI-NAS plugin, I use thin-provisioned disks only. Thin-provisioned disks tend to grow over time. If you copy a 1 GB file into a VM and you delete this file immediately, you will find that the VMDK is increased by 1 GB. This is caused by the guest filesystem. It marks the blocks of deleted files as free, even if it only deletes metadata and not the data itself. Later, the data is overwritten with new data, since the blocks are marked as free and the new data is written in there. VMware ESXi doesn’t know that the guest has marked blocks as free. So ESXi can’t shrink the thin-provisioned VMDK.

You can observe a similar behavior in case of VMFS and underlying thin-provisioned LUNs: If a VMDK is removed from a VMFS datastore, the underlying  thin-provisioned LUN doesn’t show more free space. In this case, the VAAI UNMAP primitive can be used to tell the storage system which blocks are free and can be reclaimed. Some storage system that doesn’t support VAAI UNMAP use contiguous regions filled with zeros to identify reclaimable storage space. Before free space can be reclaimed, the VMFS has to be filled with zeros. A similar technique can be used to shrink thin-provisioned guest hard disks. Please note that I don’t want to focus on reclaiming space from underlaying LUNs. I’m only talking about shrinking thin-provisioned disks!

To shrink a thin-provisioned VMDK the guest filesystem has to be zeroed out. If you use Windows, you can use SDelete. In case of a unixoide OS (Linux, FreeBSD, Solaris…), use dd. After you have zeroed out the guest file system, you have to move the VM with Storage vMotion to another datastore. Now it’s getting complicated: You have to make sure that the legacy datamover (fsdm) is used for the Storage vMotion. There are three different datamovers:

  • fsdm
  • fs3dm, and
  • fs3dm – hardware offload

The fsdm is the oldest and slowest datamover. The fs3dm and fs3dm with HW offload are newer. In case of the latter, the process is offloaded to the hardware using VAAI (Full Copy primitive). At this point, I’d like to refer to a blog post of Duncan Epping (Blocksize impact?) , who has highlighted the differences between the datamovers more detailed. The point is, that the fsdm doesn’t copy blocks that are filled with zeros. But how can I make sure, that the fsdm is used?

  • Move the VM to a datastore with another blocksize

This can be difficult, because VMFS5 datastores have a block size of 1 MB, except they were upgraded from VMFS3. Simply create a new VMFS3 datastore and use it as destination.

  • Move the VM from VMFS to NFS, from NFS to VMFS or from NFS to NFS

In this case fsdm will be used. Please note that fsdm will not be used if you move a VM from a VMFS5 to a VMFS5 datastore! In this case the fs3dm is used. This wouldn’t shrink the thin-provisioned VMDK. On the downside the fsdm is slow. Really slow. If you have a monster VM, a vMotion can take a looooong time (worth reading: “VMware Storage vMotion, Data Movers, Thin Provisioning, Barriers to Monster VM’s” by Michael Webster).

I wrote a PowerShell script that uses PowerShell remoting and VMwares PowerCLI cmdlets to do the following tasks:

  • get a list of all local disks using Get-WmiObject
  • zero-out filesystem on those disks
  • move the VM to a destination datastore
  • move the VM back to its source host and source datastore

For the moment, the script only works with Windows VMs. SDelete must be available in the VM. Make sure that you use the latest release of SDelete (currently 1.61). PowerShell remoting has to be enabled on the VMs. Feel free to use and/ or edit my script. To get this script working, please change the content of the variables for

  • $PathToSDelete
  • $VIServer
  • $CredFile
  • $Username
  • $DstDS
  • $DstDSHost and
  • $ClusterName

according to your environment. The script skips VMs with active snapshots and VMs that have one or more ZeroedThick or EagerZeroedThick disks attached. Because the script use all local disks, it will also zero-out disks that were attached using in-guest iSCSI. So please be test the script in your lab until you try it in production.

This is an example for the output of the script:

VMDK-thin-reclaim_script_output

Patrick Terlisten/ www.vcloudnine.de/ Creative Commons CC0

In this picture you can see, that the script processes one disk after another:

VMDK-thin-reclaim_performance_zero_out

Patrick Terlisten/ www.vcloudnine.de/ Creative Commons CC0

This script is provided “AS IS” with no warranty expressed or implied. Run at your own risk. Please test the script in your lab.

Load VMware PowerCLI snap-in automatically in PowerShell ISE

This posting is ~9 years years old. You should keep this in mind. IT is a short living business. This information might be outdated.

The PowerShell Integrated Scripting Environment (ISE) is a very handy application when dealing with the PowerShell. And because of this, the ISE is also a very handy application when dealing with VMware PowerCLI. When I write a script or a one-liner, one of the first things I do is to load the necessary snap-ins. And because I’m lazy, I’m trying to automate everything, what I have to do more than once. So how can I load the necessary snap-ins automatically when starting PowerShell ISE? The Windows PowerShell profile will help you. This is a simple text file, or to be more precise, a PowerShell script. Because of this, you can write everything (cmdlets, scripts, functions etc.) in this script file, and it will be executed when you start the PowerShell or the PowerShell ISE. Please note, that there are two profile files: One for the PowerShell and one for the PowerShell ISE. But where can you find the Windows PowerShell profile files? The path to the PowerShell profile is returned by the built-in variable $profile.

Open a PowerShell windows:

PS C:\Users\patrick> $profile
C:\Users\patrick\Documents\WindowsPowerShell\Microsoft.PowerShell_profile.ps1
PS C:\Users\patrick>

When you try the same in the ISE, you will get this output:

PS C:\Users\patrick> $profile
C:\Users\patrick\Documents\WindowsPowerShell\Microsoft.PowerShellISE_profile.ps1
PS C:\Users\patrick>

You see the difference? Depending on your PowerShell environment, the PowerShell reads a different profile file on startup. Usually the files doesn’t exist, except you created them. Check if the files exist. If not, this command will create an empty profile file. Depending on if the command is executed in a PowerShell windows or in the PowerShell ISE, a profile file for PowerShell or PowerShell ISE is created.

PS C:\Users\patrick> Test-Path $profile
False

PS C:\Users\patrick> New-Item -path $profile -type file -force | Out-Null

PS C:\Users\patrick> Test-Path $profile
True

PS C:\Users\patrick>

Close the PowerShell ISE. Now you can open the file with your favorite editor and add the command Add-PsSnapin.

# Load Windows PowerShell cmdlets for managing vSphere
Add-PsSnapin VMware.VimAutomation.Core -ea "SilentlyContinue"

Save the file and open the PowerShell ISE. A Get-PSSnapin should return, that the VMware.VimAutomation.Core module was loaded. You should also notice that you can now use PowerCLI cmdlets. Everytime you open the PowerShell ISE, the VMware.VimAutomation.Core snap-in is automatically loaded.

Backup DataCore SANsymphony-V config using PowerShell

This posting is ~9 years years old. You should keep this in mind. IT is a short living business. This information might be outdated.

In November 2013 I published a PowerShell script on blazilla.de that creates a backup of your SANsymphony-V config by using the DataCore SANsymphony-V PowerShell cmdlets. I would like to thank Marcel, Michael and Frank for their feedback and comments to improve the script. The password is stored in the securestring.txt that needs to be stored in the same directory as the script. Kudos to Marcel, who has the part of the script contributed, that stores the password in an encrypted file.

Currently there is no way to schedule a configuration backup using the DataCore GUI. A valid backup of the configuration can be a lifesaver, especially in the case, that you storage server crashed and it has to be re-installed. So it’s a good idea to regularly backup the configuration to the local disk and copy this backup to another location. One way to create a backup, is an Outlook task and the DataCore GUI. Belive me, that will not work… At this point my script comes into play. The script uses Microsoft PowerShell, DataCore SANsymphony-V PowerShell cmdlets and the Windows Task Scheduler to automate the backup of SANsymphony-V configuration.

There are a couple of variables used in the script. Two of them are used to change the number of stored backups and the directory in which the backups will be saved. The script saves the last three versions of the configuration backups, but you can change this with the $Keep variable. The directory, in which the backup will be saved, is influenced by the $BackupFolder variable.

You can call the script using the Taskplanner with the following command:

powershell.exe -c "C:\Scripts\Backup-DataCoreConfig.ps1 | Out-Null"

You can select a different directory than C:\Scripts. The “Out-Null” behind the pipe symbol suppresses the output of the script.

My most frequently used PowerCLI One-liner

This posting is ~9 years years old. You should keep this in mind. IT is a short living business. This information might be outdated.

Over the last months I wrote different PowerCLI One-liners who I want to share. Nothing fancy and one or two are ugly. But they worked for me. :)

Changing the multipathing policy for all hosts and datastores in a cluster

Get-Cluster PROD | Get-VMhost | Get-scsiLun -CanonicalName “naa.60030*”| Set-ScsiLun -MultipathPolicy "roundrobin"

Get a list of all VMs in a cluster and the datastore in which the VMs resides

Get-Cluster | Get-VM | select name, @{N="Datastore";E={Get-Datastore -VM $_}} | sort name

Get a list of all VMs, their mac-address and the connected port groups

Get-VM | Select Name, @{N="Network Adapter";E={$_.NetworkAdapters| foreach-object {$_.Type}}}, @{N="MacAddress";E={$_.NetworkAdapters| ForEach-Object {$_.MacAddress}}}, @{N="PortGroup";E={Get-VirtualPortGroup -VM $_}}

vMotion of a VM between hosts without a shared storage (not really a One-liner…)

Move-VM -Destination esx-lab-01.testlab.site -Datastore local_ESX_LAB_01 -VM TSTCLN02

Enable SSH on all hosts

Get-VMHost | Foreach {Start-VMHostService -HostService ($_ | Get-VMHostService | Where { $_.Key -eq "TSM-SSH"} )}

Check on which hosts SSH is enabled

Get-VMHost | Get-VMHostService | Where { $_.Key -eq "TSM-SSH" } |select VMHost, Label, Running

Get a list of hosts and the numer of VM that are running on these hosts

Get-VMHost | Sort-Object Name | Select Name,@{N="VM";E={ if ($_.ExtensionData.Vm -ne $null) { $_.ExtensionData.Vm.Count } else {0}}}

If you’re searching for more advanced PowerCLI stuff visit the blogs of Alan Renouf and Luc Dekens.