Category Archives: VMware

vSphere Reports in Microsoft Teams

By joining PowerCLI to interrogate a VMware vSphere environment and the Webhook API to post Microsoft Teams messages, we can send informational reports on the state of a virtual environment to channels in Teams.

In this example I have a weekly report on the number of hosts and VMs in an environment. This is fairly straightforward, but more complicated information surfaced by PowerCLI could easily be included.

vistatsinteams

Setting up the WebHook

The Webhook allows messages to be pulled into the Teams channel of choice. You’ll need to setup an Incoming Webhook on your channel of choice.

To do this

  1. Right-click on the Teams Channel and choose Connectors
  2. Find Webhook in the list and click on the adjacent Confugure button.
  3. Enter a name, and choose an icon for the Webhook as prompted. Click on Create and a URL will be generated- this is needed for the script below.

Storing vCenter credentials

As this will be run as a scheduled task we need to store credentials so that the script doesn’t try to prompt for a password. There’s a number of ways of doing this- here I’ve used the Get-Credential cmdlet to prompt for the username and password and then the Export-CliXml cmdlet to save those credentials to a file.

 $Credential = Get-Credential

$Credential |

Export-CliXml -Path C:\Users\myuser\readonlyuser.cred

For the vCenter operations in the script below (counting hosts and VMs) then a user account with Read-Only access is sufficient.

The Script

Finally the script. This can be run manually or set as a scheduled task. The URL of the webhook created above needs to be entered as the value of the $TeamsWebhookURI variable at the top, along with the vCenter credentials and the name of the vCenter.

#Set the Webhook URI to send the message to
$TeamsWebhookURI="https://outlook.office.com/webhook/[email protected]/IncomingWebhook/zzzzzzzzz/zzzzz-zzzz"
#Use pre-defined credentials
$credential = import-clixml -Path C:\Users\myuser\readonlyuser.cred
#What is the address of the vCenter?
$vCenter="vcenter.mydomain.com"
#Connect to vCenter
Connect-VIServer -Server $vCenter -Credential $credential
#Count the VMs and hosts
$poweredoff = @(get-vm | Where-object{$_.PowerState -ne "PoweredOn" } | measure-Object -Line).Lines
$poweredon = @(get-vm | Where-object{$_.PowerState -eq "PoweredOn" } | measure-Object -Line).Lines
$hostCount=(Get-VMHost | Measure-Object).Count
#Disconnect from vCenter
Disconnect-viserver -Server $vCenter -Confirm:$false

#Build the JSON for the Body of the message
$JSONBody = [PSCustomObject][Ordered]@{
"@type" = "MessageCard"
"@context" = "http://schema.org/extensions"
"summary" = "Weekly Infrastructure Stats"
"themeColor" = '0078D7'
"sections" = @(
@{
"activityTitle" = "Virtual Infrastructure"
"activitySubtitle" = "Statistics"
"activityImage" = "https://mydomain.com/infoicon.svg"
"facts" = @(
@{
"name" = "Num. of Hosts: "
"value" = $hostcount
},
@{
"name" = "Powered On VMs: "
"value" = $poweredon
},
@{
"name" = "Powered Off VMs: "
"value" = $poweredoff
},
@{
"name" = "Total VMs: "
"value" = ($poweredon+$poweredoff)
}
)
"markdown" = $true
}

)
}
$TeamMessageBody = ConvertTo-Json $JSONBody -Depth 100

#Build the API call
$parameters = @{
"URI" = $TeamsWebhookURI
"Method" = 'POST'
"Body" = $TeamMessageBody
"ContentType" = 'application/json'
}
#Make the API Call
Invoke-RestMethod @parameters

The input arguments had entities that did not belong to the same datacenter.

Symptoms

Trying to vMotion (in my case a cross-vCenter vMotion from a 6.5 system to 6.7) a VM the error “The input arguments had entities that did not belong to the same datacenter.” occurred. Other VMs were migrating fine.

image

Cause

The CD Drive in the source VM was mapped to (although not connected to) a Content Library ISO file.

Solution

Point the source VM CD drive at “Client Device” and retry the vMotion.

image

PowerShell Get-Command: finding the cmdlet

A recent Slack chat reminded me that PowerShell’s Get-Command cmdlet is a good way of finding what commands to use when you encounter a new problem. However it goes beyond typing “Get-Command” and just getting a huge list back- my laptop just gave me 7659 commands to choose from – as this can be unusable. Here’s some quick tips on focussing your search by using the built in arguments.

1. –module

PowerShell and it’s extensions are comprised of modules. If you want to use the cmdlets for interacting with a VMware environment you install their “PowerCLI” module. Get-Command can return just the cmdlets from a specific module, for example we can list all the cmdlets from the VMware modules

Get-Command –Module VMware.*

Or we can list the commands in the Azure Compute PowerShell module

Get-Command –Module Az.Compute

2. –verb

If you’ve used PowerShell before, you’ll know that cmdlet names are all of the format verb (“a doing word” as I was taught at school), followed by a dash,  followed by a noun. So we have Measure-Object, Remove-Disk, and even Get-Command itself. The “-verb” argument can be used to only show us cmdlets with this verb, for example to only see the “Get” cmdlets we use

Get-Command –Verb Get

3. –noun

So, after the dash we have the noun. A disk, network connection, user account, and so on. So to find out all the cmdlets that work on or with services:

Get-Command –Noun Service

4. Combining the above

Of course we can make this even more powerful by combining these arguments together and with wildcards. Let’s say we want to know all the cmdlets for working with VMware vSphere tags?

Get-Command –Module VMware* –Noun *Tag*

Or if we want to find all the get Azure get commands for working with resources, resource groups, resource locks and so on.

Get-Command -Module Az.* -Verb Get -Noun *resource*