Powershell: Creating strong passwords

A tweet from @JanEgilRing caught my eye this morning, it was showing how you can use powershell to create passwords. The link in the tweet pointed here: http://powershell.com/cs/blogs/tips/archive/2016/05/23/one-liner-random-password-generator.aspx

Seeing that line and realizing how simple it was, it got me thinking on how I could implement this in my scripts.
The only issue I saw with that one-liner was that the passwords it creates do not necessarily comply with high complexity rules.

So, how can we approve on this?

Firstly, we need to create a regex that we can use to validate that the password created complies with our rules.
In our environment this means 12 characters, uppercase, lowercase and either a number or special character.
The regex I ended up with is this one: ^.*(?=.{12,})(?=.*\d)(?=.*[a-z])(?=.*[A-Z])(?=.*[@#$%^&+=]).*$
(which I found here: https://nilangshah.wordpress.com/2007/06/26/password-validation-via-regular-expression/ )

Now that we have our regex we can simple throw the one-liner into a while loop:

while ($pass -notmatch "^.*(?=.{12,})(?=.*\d)(?=.*[a-z])(?=.*[A-Z])(?=.*[@#$%^&+=]).*$") {
 $pass = -join ('abcdefghkmnrstuvwxyzABCDEFGHKLMNPRSTUVWXYZ23456789$%&*#'.ToCharArray() | Get-Random -count 12)
 }

This means that as long as the password created doesn’t comply with the regex, it creates a new one.

And guess what? It can also be written as a one-liner:

while ($pass -notmatch "^.*(?=.{12,})(?=.*\d)(?=.*[a-z])(?=.*[A-Z])(?=.*[@#$%^&+=]).*$") {$pass = -join ('abcdefghkmnrstuvwxyzABCDEFGHKLMNPRSTUVWXYZ23456789$%&*#'.ToCharArray() | Get-Random -count 12)}

Not exactly a simple one-liner, but a one-liner still Smilie: :)

Posted in Microsoft, Powershell Tagged , ,

UCSD: Mapping EMC VNX LUNs to an ESXi cluster

I have been struggling for quite some time with mapping luns from our vnx 5600 to entire clusters in our vCenter. We used to utilize a custom workflow a consultant wrote for us, but that workflow got borked after an update to UCS Director nearly a year ago.
Revisiting the issue i found this example from Cisco: https://communities.cisco.com/docs/DOC-57382

That example seems to work for other people but in our case the custom task in it never gave the correct output, so I had to look for a way around it.

The solution I came up with is overly complicated and can surely be simplified, but my lacking knowledge of javascript limits me quite a bit. My workflow to map luns to vSphere clusters consists roughly of these steps:

  • A powershell task running a script that does the following:
    • Queries vCenter for esxi hosts in given cluster
    • Queries UCS Directors api for a report on storage groups
    • Putting together a storage group identity for each esxi host
    • Returning all storage group identities in a comma separated matter
  • A custom task to parse and convert the output from the powershell task to an output of the emcStorageGroupIdentity type
  • The builtin “add vnx lun to storage group” task

Here’s how I have set it up:

2015-10-15_14-48-58

The custom task I use to convert the output from the powershell task is built up the way I describe in this blogpost: http://cloud.kemta.net/cisco-network/ucsd-grabbing-a-string-returned-from-a-powershell-task/

The only difference is the name and type of the output.

Now, let’s look at the workflow user inputs:

2015-10-15_14-17-37

In this workflow I haven’t configured any outputs, so let’s move along to the first task: the powershell task.

2015-10-15_14-41-13

The reason I have to do a split on the StorageAccount input is that in the script I only want the hostname of the storage system. I could just as well have done it within the script itself.

The powershell script looks like this:

Param (
    [Parameter(Mandatory=$True,Position=0)][string]$Pod,
    [Parameter(Mandatory=$True,Position=1)][string]$StorageAccount,
    [Parameter(Mandatory=$True,Position=2)][string]$Cluster
        )
#Add the vmware snapin
Add-PSSnapin vmware*
 
#Connecting to vCenter
Connect-VIServer vcenter01 -WarningAction Ignore | out-null
 
#Getting ESXi hosts
$vmhosts = get-vmhost -location $Cluster
 
#Polling a report on storage groups from UCS Director
$webrequest = Invoke-WebRequest 'https://ucsd01/app/api/rest?opName=userAPIGetTabularReport&opData={param0:"510",param1:"$($StorageAccount);$($Pod)",param2:"STORAGE-GROUPS-T51"}' -Headers @{"X-Cloupia-Request-Key"="ThisIsNotMyAPIKey"}
#Converting the report from json
$convertedData = $webrequest.content | ConvertFrom-Json
 
#Creating a blank array to store end result in
$VMhostsArray = @()
 
#Looping through all esxi hosts found
foreach ($vmhost in $vmhosts) {
    $hostname = (($vmhost.name).split("."))[0] #Removing domain name from esxi host name
    $WWN_Name = ($convertedData.serviceresult.rows | where {$_.Name -like "$($hostname)"}).WWN_Name #Grabbing WWN from UCSD report
    $StorageGroupIdentity = "$($StorageAccount);$($Pod);$($WWN_Name);$($hostname)" #Putting together the storage group identity
    $VMhostsArray += $StorageGroupIdentity #Adding the storage group identity to the array
    }
 
#Disconnecting from vCenter
Disconnect-VIServer vcenter01 -Confirm:$false
 
#Returning array to console, comma separated
return $VMhostsArray -join ","

On my test cluster with only two esxi hosts, the output looks like this:

2015-10-15_14-54-42

After the custom task has converted the powershell output it looks like this in plain text:

storageaccount;pod;wwn;esxiHostname1,storageaccount;pod;wwn;esxiHostname2

Since the output from the custom task is of the emcStorageGroupIdentity type I can simply map it to the builtin “add vnx lun to storage group” task:

2015-10-15_14-47-52

And that’s really all there is to it. If you want to download my example workflow, you can do so here: http://cloud.kemta.net/wp-content/uploads/2015/10/mapVNXLunToESXiCluster.zip

 

Posted in Cisco, UCS Director Tagged , , , , ,

UCSD: Grabbing a string returned from a Powershell task

My last post described how to get around some issues with using Powershell tasks in workflows. While that post surely enables you to uilize powershell to do stuff for you, what about if you want Powershell to grab stuff for you and return them in a usable matter?
This time I’m going to show you how you can return a string from Powershell and use it further down in the workflow. Cisco has provided an example on how to do that here: https://communities.cisco.com/docs/DOC-58250

The example from Cisco is what I started with, but I have modified it a bit since I didn’t want anything that advanced.

So let’s set the stage:
Say you have a workflow that uses the execute powershell command task and you want that task to output something you can utilize further down  in the workflow, e.g. sending that output in an email. In this case we will use powershell to give us a comma separated list of esxi hosts in a given cluster.
The workflow has 1 defined workflow user input, vspherecluster, and 1 defined workflow user output, PowerShell_output.

There are a few things we need to get this done:

  • A powershell script that only outputs one line with all the hosts
  • A powershell task that runs said script
  • A custom tasks that parses the powershell output and only returns the string returned by the powershell script
  • A send email task sending the output

First off, the powershell script:

Param (
 [Parameter(Mandatory=$True,Position=0)][string]$Cluster
 )
#Add the vmware snapin
Add-PSSnapin vmware*
 
#Connecting to vCenter
Connect-VIServer drt01srv003 -WarningAction Ignore | out-null
 
#Getting ESXi hosts
$vmhosts = get-vmhost -Location $cluster
 
#Creating a blank array to put hostnames in
$hostarray =@()
 
#Looping through all ESXi hosts and adding their names to the array
foreach ($vmhost in $vmhosts) {
 $hostarray += $vmhost.name
 }
 
#Disconnecting from vCenter
Disconnect-VIServer drt01srv003 -Confirm:$false
 
#Comma separaing the array and returning it to console
return $hostarray -join ","

The comments should be pretty explanatory, but in essence it connects to vCenter, grabs the name of the esxi hosts in the given cluster and then returns them comma separated.
This works fine and dandy in a powershell console, but the output from the powershell task will look rather different, we’ll look at that in a second. Secondly, the powershell task kicking off the script:

2015-10-14_13-41-33

The output from this task will look something like this:2015-10-14_14-02-07

If you’re unfamiliar with how UCS Director handles powershell but familiar with powershell, then that’s probably not what you expected at all.

Now we need to parse that output and return only the string we want. I made a custom task for this, like in the example from Cisco:

Required inputs:
2015-10-14_13-42-43

Outputs:
2015-10-14_13-43-04

The beauty of doing it like this is that you can create any kind of output type just by changing the output type. I actually use this to create outputs of other types as well, for example vmwareHostNodeIdentity, vmwareClusterIdentity and so on. It’s just easier for me to use powershell to put those kind of outputs together instead of using javascript since I really don’t know javascript.

And then the script:

importPackage(com.cloupia.lib.util);
importPackage(java.util);
 
var xml = input.xml;
 
// Try and parse the <Objects>...</Objects> section
var objects_xml = XMLUtil.getValue("Objects", xml);
logger.addDebug("Grabbed objects from xml: "+objects_xml);
 
// Parse the objects list now (should also be a single section):
object_list = XMLUtil.getTag("Object",objects_xml.get(0))
logger.addDebug("Grabbed object_list from xml: "+object_list);
 
//Convert object_list to string
list = String(object_list);
logger.addDebug("list: "+list);
 
// Slice off last xml characters
parsedString = list.substring(30);
logger.addDebug("After removing first characters: "+parsedString);
parsedString = parsedString.slice(0,-10);
logger.addDebug("After removing last characters: "+parsedString);
 
output.parsedString = parsedString;

When using the custom task in a workflow I map the input to the output from the powershell task:

2015-10-14_13-43-32

And the output to the defined workflow user output:

2015-10-14_13-43-56

Lastly, it’s the send email task which is pretty simple:

2015-10-14_13-44-27

If you want to test this yourself, you can download this example workflow here: http://cloud.kemta.net/wp-content/uploads/2015/10/getPowershellOutput.zip

Posted in Cisco, UCS Director Tagged , , , ,

UCSD: Passing multiple arguments to start-job while using the Cisco PowerShell Agent

While the Cisco PowerShell Agent (PSA) that can be used in UCS Director isn’t exactly perfect, it can still be put to good use. As long as you now how to use it properly Smilie: ;)

The major issue with using the PSA is that it doesn’t stick around to see if the commands/script was successful or not. As long as it delivered the commands successfully, it’s happy and your workflow will continue to the next step.

Jon Hildebrand describes a nice way around this in one of his blog posts: http://snoopj.wordpress.com/2014/11/05/cisco-powershell-agent-service-and-vmware-vum-powercli/

Using his approach, I was able get the PSA to stick around until the job finishes. However, I ran into a challenge when I wanted to pass multiple arguments to start-job. The solution I came up with was declaring the UCSD inputs I wanted to use as powershell variables in the script, before calling the start-job cmdlet. So the commands/script input looks like this:

$vm = "${custom_getVMDetails_7494.vmName}"
$annotation = "${Annotation}"
$annotationvalue = "${AnnotationValue}"
Start-Job -FilePath "C:\Powershell\Annotate-VM.ps1" -ArgumentList $vm,$annotation,$annotationvalue | Wait-Job

In case you are wondering, this task is for setting annotations on a vm in vCenter.
The powershell script I am calling is pretty simple:

Param (
 [Parameter(Mandatory=$True,Position=0)][string]$VM,
 [Parameter(Mandatory=$True,Position=1)][string]$Annotation,
 [Parameter(Mandatory=$True,Position=2)][string]$AnnotationValue
 )
Add-PSSnapin vmware*
Connect-VIServer 
Set-Annotation -Entity $VM -CustomAttribute "$($Annotation)" -Value "$($AnnotationValue)"
Disconnect-VIServer  -Confirm:$false

Posted in Cisco, UCS Director Tagged , , , ,

PowerCLI: Function for listing snapshots

My very first PowerCLI related post was about this same topic: listing snapshot info using PowerCLI.
In my original post (which you can see here) I only wrote a pretty simple one-liner. Which was kind of okay, but it was missing one crucial thing: who took the snapshot?

Why vmware hasn’t found a way to include a username in the get-snapshot cmdlet is something I just can’t understand. There’s really not much code needed to add this to the output, and there’s several ways of doing so.

I found that using Get-Snapshot and Get-VIEvent together was the easiest way to get all the info I want. It’s not a perfect solution, seeing as I really wanted to make use of the much faster Get-View instead of Get-Snapshot, but I have yet to figure out a good way to handle snapshot trees using Get-View.

As usual I created a function for this, Get-Snapshots:

function Get-Snapshots 
{
            [CmdletBinding()] 
            Param (
                [string]$VM = '*'
                  )
 
    $collection = @()
    Write-Progress -Activity "Finding snapshots..." -Status "This will tak a while, please wait" -PercentComplete 20 -Id 1 -ErrorAction SilentlyContinue
    $snapshots = Get-Snapshot -vm $VM
    Write-Progress -Activity "Finding snapshots..." -Status "Found all snapshots" -Completed -Id 1 -ErrorAction SilentlyContinue
 
    $progress = 1
    foreach ($snapshot in $snapshots) {
        Get-VIEvent -Start ($snapshot.Created).addminutes(-5) -Finish ($snapshot.Created).addminutes(5) -Entity $Snapshot.vm.name -Types info -maxsamples 20 | Where-Object {$_.FullFormattedMessage -like "*Create virtual machine snapshot*"} | ForEach-Object {
                Write-Progress -Activity "Finding snapshots" -Status "Working on $($_.Vm.Name)" -PercentComplete ($progress/$snapshots.count*100) -Id 1 -ErrorAction SilentlyContinue
                $object = New-Object PSObject
                Add-Member -InputObject $object NoteProperty VM $_.Vm.Name
                Add-Member -InputObject $object NoteProperty User $_.Username
                Add-Member -InputObject $object NoteProperty "Snapshot name" $Snapshot.Name
                Add-Member -InputObject $object NoteProperty "Snapshot description" $Snapshot.Description
                Add-Member -InputObject $object NoteProperty SizeGB ([math]::Round($Snapshot.SizeGB))
                Add-Member -InputObject $object NoteProperty Time $_.CreatedTime
                $collection += $object
                }
        $progress++
        }
 
    Write-Progress -Activity "Finding snapshots" -Status "All done" -Completed -Id 1 -ErrorAction SilentlyContinue
    $collection
 
    <#
     .Synopsis
      Lists snapshots in vCenter
     .Description
      List all snapshots in the entire vCenter
     .Example
      Get-Snapshots
      Lists all snapshots in the vCenter
     .Link
      http://cloud.kemta.net
     #>
}

You can run the function as it is, without any parameters, or you can specify which vm you want to get the snapshots of:

get-snapshots

As you can see, the output is a list. If you want it to be more readable for human eyes, I recommend piping it to Format-Table:

get-snapshots-ft

If you would rather run this as a script, instead of using the function, here’s the code for that:

    $collection = @()
    $snapshots = Get-Snapshot -vm *
 
    $progress = 1
    foreach ($snapshot in $snapshots) {
        Get-VIEvent -Start ($snapshot.Created).addminutes(-5) -Finish ($snapshot.Created).addminutes(5) -Entity $Snapshot.vm.name -Types info -maxsamples 20 | Where-Object {$_.FullFormattedMessage -like "*Create virtual machine snapshot*"} | ForEach-Object {
                Write-Progress -Activity "Finding snapshots" -Status "Working on $($_.Vm.Name)" -PercentComplete ($progress/$snapshots.count*100) -Id 1 -ErrorAction SilentlyContinue
                $object = New-Object PSObject
                Add-Member -InputObject $object NoteProperty VM $_.Vm.Name
                Add-Member -InputObject $object NoteProperty User $_.Username
                Add-Member -InputObject $object NoteProperty "Snapshot name" $Snapshot.Name
                Add-Member -InputObject $object NoteProperty "Snapshot description" $Snapshot.Description
                Add-Member -InputObject $object NoteProperty SizeGB ([math]::Round($Snapshot.SizeGB))
                Add-Member -InputObject $object NoteProperty Time $_.CreatedTime
                $collection += $object
                }
        $progress++
        }
 
    Write-Progress -Activity "Finding snapshots" -Status "All done" -Completed -Id 1 -ErrorAction SilentlyContinue
    $collection

The output, $collection, can be piped to Format-Table if you want a nice table of it:

get-snapshots-scripts

 

Posted in PowerCLI, Powershell, VMware Tagged , , , , , , ,

PowerCLI: Evacuating a datastore

In case you ever need to empty out a datastore in you vmware environment, there is a nice little one-liner in PowerCLI for that:

Get-VM -Datastore "datastore1" | Move-VM -Datastore (Get-VMHost -Location 'cluster1' | Select-Object -First 1 | Get-Datastore | Where-Object {($_.Name -ne 'datastore1') -and ($_.FreeSpaceGB -gt '500')} | Sort-Object FreeSpaceGB -Descending | Select-Object -First 1)

Where “datastore1” is the datastore you want to empty out and “cluster1” is the cluster where the datastore is available.

The command will move VMs from datastore1 to the datastore in cluster1 with the most available space (minimum 500GB)

Posted in PowerCLI, Powershell, VMware Tagged , , , ,

PowerCLI: Listing VMs with ISOs mounted

For almost a year ago, I posted a simple one-liner to list all VMs who has ISOs mounted. You can view that post here: http://cloud.kemta.net/2013/10/powershell-vmware-list-all-vms-with-iso-mounted-and-dismount-them/

That post was written before I truly discovered the major advantages of using Get-View instead of Get-VM, Get-VMHost and so on. If used correctly, there’s a major difference in speed when using Get-View over Get-VM.
When writing this post I checked the differences in speed when using the old way that I linked to above and my new function (which I’ll get to in a second or two..), the result was as follows:

get-isomounts_measured

As you can see, the difference is pretty clear. 5 seconds vs. 1.6 minutes…

So, without further ado, I present to you the code for Get-ISOMounts:

function Get-ISOMounts
{
    [CmdletBinding()] 
        Param (
            [switch]$Dismount
              )
        $VMs = Get-View -ViewType virtualmachine -Property name,Config.Hardware.Device
        $VMsWithISO = @()
        $progress = 1
        foreach ($VM in $VMs) {
            Write-Progress -Activity "Checking if VMs have ISOs mounted" -Status "Working on $($VM.name)" -PercentComplete ($progress/$VMs.count*100) -Id 1 -ErrorAction SilentlyContinue
            if (($VM | select -ExpandProperty config | select -ExpandProperty hardware | select -ExpandProperty device | select -ExpandProperty deviceinfo | where {$_.Summary -like "ISO*"}) -ne $NULL) {
                $object = New-Object PSObject
                Add-Member -InputObject $object NoteProperty VM $VM.Name
                Add-Member -InputObject $object NoteProperty "ISO mounted" (($VM | select -ExpandProperty config | select -ExpandProperty hardware | select -ExpandProperty device | select -ExpandProperty deviceinfo | where {$_.Summary -like "ISO*"}).Summary).Substring(4)
                $VMsWithISO += $object
                $object
            $progress++
            }
        }
        Write-Progress -Activity "Checking if VMs have ISOs mounted" -Status "All done" -Completed -Id 1 -ErrorAction SilentlyContinue
 
        if ($Dismount)
            {
            Write-Verbose "Starting to dismount ISOs"
            $progress = 1
            foreach ($VM in $VMsWithISO) {
                Write-Progress -Activity "Dismounting ISOs" -Status "Working on $($VM.name)" -PercentComplete ($progress/$VMsWithISO.count*100) -Id 1 -ErrorAction SilentlyContinue
                Get-CDDrive -VM $VM.Name | Set-CDDrive -NoMedia -Confirm:$False
                }
            Write-Progress -Activity "Dismounting ISOs" -Status "All done" -Completed -Id 1 -ErrorAction SilentlyContinue
            $progress++
            }
 
<#
 .Synopsis
  Lists all VMs with ISOs mounted, can also dismount them
 .Description
  Lists all VMs with ISOs mounted. If the switch -Dismount is present all mounted ISOs will be dismounted
 .Example
  Get-ISOMounts
  Lists all mounted ISOs in the vCenter
 .Example
  Get-Snapshots -Dismount
  Lists all mounted ISOs on VMs in the vCenter and then dismounts them
 .Link
  http://cloud.kemta.net
 #>
}

I feel the help section should speak for itself, but I’ll provide you a screenshot none the less. Just running Get-ISOMounts will provide you with an output looking like this:

get-isomounts

Posted in PowerCLI, Powershell, VMware Tagged , , , , , ,

PowerCLI: Getting vmhost uptime

I love a powershell challenge, and last week a colleague of mine asked me for assistance in getting the uptime of vmware hosts. My initial response did the trick:

Get-View  -ViewType hostsystem -Property name,runtime.boottime | Select-Object Name, @{N="UptimeDays"; E={((((get-date) - ($_.runtime).BootTime).TotalDays).Tostring()).Substring(0,5)}}

However, I wasn’t completely satisfied by the the output or the ease of use.
So today I went back and rewrote the code and made a function of it.
Instead of using the ToString and Substring methods I went for the built-in class Math, which has a method called Round. You can learn more about the Math class here: http://www.madwithpowershell.com/2013/10/math-in-powershell.html

Anyways, here’s the function I came up with:

function Get-VMHostUptime
    {
        [CmdletBinding()] 
            Param (
                [Parameter(ValueFromPipeline=$True,ValueFromPipelineByPropertyName=$True)][Alias('Name')][string]$VMHosts,
                [string]$Cluster
                  )
    Process{
         If ($VMHosts) {
            foreach ($VMHost in $VMHosts) {Get-View  -ViewType hostsystem -Property name,runtime.boottime -Filter @{"name" = "$VMHost"} | Select-Object Name, @{N="UptimeDays"; E={[math]::round((((Get-Date) - ($_.Runtime.BootTime)).TotalDays),1)}}, @{N="UptimeHours"; E={[math]::round((((Get-Date) - ($_.Runtime.BootTime)).TotalHours),1)}}, @{N="UptimeMinutes"; E={[math]::round((((Get-Date) - ($_.Runtime.BootTime)).TotalMinutes),1)}}}
            }
 
         elseif ($Cluster) {
            foreach ($VMHost in (Get-VMHost -Location $Cluster)) {Get-View  -ViewType hostsystem -Property name,runtime.boottime -Filter @{"name" = "$VMHost"} | Select-Object Name, @{N="UptimeDays"; E={[math]::round((((Get-Date) - ($_.Runtime.BootTime)).TotalDays),1)}}, @{N="UptimeHours"; E={[math]::round((((Get-Date) - ($_.Runtime.BootTime)).TotalHours),1)}}, @{N="UptimeMinutes"; E={[math]::round((((Get-Date) - ($_.Runtime.BootTime)).TotalMinutes),1)}}}
            }
 
         else {
            Get-View  -ViewType hostsystem -Property name,runtime.boottime | Select-Object Name, @{N="UptimeDays"; E={[math]::round((((Get-Date) - ($_.Runtime.BootTime)).TotalDays),1)}}, @{N="UptimeHours"; E={[math]::round((((Get-Date) - ($_.Runtime.BootTime)).TotalHours),1)}}, @{N="UptimeMinutes"; E={[math]::round((((Get-Date) - ($_.Runtime.BootTime)).TotalMinutes),1)}}
            }
        }
<#
 .Synopsis
  Shows the uptime of VMHosts
 .Description
  Calculates the uptime of VMHosts provided, or VMHosts in the cluster provided
 .Parameter VMHosts
  The VMHosts you want to get the uptime of. Can be a single host or multiple hosts provided by the pipeline
 .Example
  Get-VMHostUptime
  Shows the uptime of all VMHosts in your vCenter
 .Example
  Get-VMHostUptime vmhost1
  Shows the uptime of vmhost1
 .Example
  Get-VMHostUptime -cluster cluster1
  Shows the uptime of all vmhosts in cluster1
 .Example
  Get-VMHost -location folder1 | Get-VMHostUptime
  Shows the uptime of VMHosts in folder1
 .Link
  http://cloud.kemta.net
 #>
}

You can use it in a couple of different ways, as documented in its help section (get-help get-vmhostuptime -expamples). But if you provide no input to it, it will generate an output like this:

get-vmhostuptime

Hopefully, you don’t have an uptime as long as some of these hosts Smilie: :)

Posted in PowerCLI, Powershell, VMware Tagged , , , , , ,

PowerCLI: Getting the status of vmware tools on all VMs

I’m sure I don’t need to explain to you guys why VMware tools is a good idea to have installed on your VMs, and probably not why it’s a good idea to keep VMware tools updated.
However, I haven’t found a good way to get a neat list of which VMs need to have their VMware tools upgraded. While working on my vCenter health check script I found that I had to make my own little script to get that list. And, in addition, I wanted the list to include the VM version.

I ended up with creating a function to provide me with that list:

function Get-VMToolsStatus
    {
        [CmdletBinding()] 
        Param (
            [ValidateSet('NeedUpgrade','NotInstalled','Unsupported')][string]$Filter
            )
 
    $VMs = Get-View -ViewType VirtualMachine -Property name,guest,config.version,runtime.PowerState
    $report = @()
    $progress = 1
    foreach ($VM in $VMs) {
        Write-Progress -Activity "Checking vmware tools status" -Status "Working on $($VM.Name)" -PercentComplete ($progress/$VMs.count*100) -ErrorAction SilentlyContinue
        $object = New-Object PSObject
        Add-Member -InputObject $object NoteProperty VM $VM.Name
        if ($VM.runtime.powerstate -eq "PoweredOff") {Add-Member -InputObject $object NoteProperty ToolsStatus "$($VM.guest.ToolsStatus) (PoweredOff)"}
        else {Add-Member -InputObject $object NoteProperty ToolsStatus $VM.guest.ToolsStatus}
        Add-Member -InputObject $object NoteProperty ToolsVersionStatus ($VM.Guest.ToolsVersionStatus).Substring(10)
        Add-Member -InputObject $object NoteProperty SupportState ($VM.Guest.ToolsVersionStatus2).Substring(10)
        if ($object.ToolsStatus -eq "NotInstalled") {Add-Member -InputObject $object NoteProperty Version ""}
        else {Add-Member -InputObject $object NoteProperty Version $VM.Guest.ToolsVersion}
        Add-Member -InputObject $object NoteProperty "HW Version" $VM.config.version
        $report += $object
        $progress++
        }
    Write-Progress -Activity "Checking vmware tools" -Status "All done" -Completed -ErrorAction SilentlyContinue
 
    if ($Filter -eq 'NeedUpgrade') {
        $report | Sort-Object vm | Where-Object {$_.ToolsVersionStatus -eq "NeedUpgrade"}
        }
    elseif ($Filter -eq 'NotInstalled') {
        $report | Sort-Object vm | Where-Object {$_.ToolsVersionStatus -eq "NotInstalled"}
        }
    elseif ($Filter -eq 'Unsupported') {
        $report | Sort-Object vm | Where-Object {($_.SupportState -eq "Blacklisted") -or ($_.SupportState -eq "TooNew") -or ($_.SupportState -eq "TooOld") -or ($_.SupportState -eq "Unmanaged")}
        }
    else {$report | Sort-Object vm}
 
<#
 .Synopsis
  List vm tools status for all VMs
 .Description
  Lists the status and version for all VMs, also tells whether the version is supported and also the vm version
 .Parameter Filter
  Filters the list based on if the VMware tools "NeedUpgrade", is "NotInstalled" or if they are "Unsupported"
 .Example
  Get-VMToolsStatus
  List vm tools status for all VMs
 .Example
  Get-VMToolsStatus -Filter NeedUpgrade
  Show only VMs needing update of vm tools
 .Link
  http://cloud.kemta.net
 #>
}

The function will create an output that look kinda like this:

get-vmtoolsstatues

You can also use a basic filter on the function by specifying -Filter <filtername>. The different filters is documented in the help section.
For example, if you only want a list of VMs who need an upgrade of their VMware tools version:

get-vmtoolsstatus-needupgrade

In case you don’t want to use this as a function, here’s the script version:

    $VMs = Get-View -ViewType VirtualMachine -Property name,guest,config.version,runtime.PowerState
    $report = @()
    $progress = 1
    foreach ($VM in $VMs) {
        Write-Progress -Activity "Checking vmware tools status" -Status "Working on $($VM.Name)" -PercentComplete ($progress/$VMs.count*100) -ErrorAction SilentlyContinue
        $object = New-Object PSObject
        Add-Member -InputObject $object NoteProperty VM $VM.Name
        if ($VM.runtime.powerstate -eq "PoweredOff") {Add-Member -InputObject $object NoteProperty ToolsStatus "$($VM.guest.ToolsStatus) (PoweredOff)"}
        else {Add-Member -InputObject $object NoteProperty ToolsStatus $VM.guest.ToolsStatus}
        Add-Member -InputObject $object NoteProperty ToolsVersionStatus ($VM.Guest.ToolsVersionStatus).Substring(10)
        Add-Member -InputObject $object NoteProperty SupportState ($VM.Guest.ToolsVersionStatus2).Substring(10)
        if ($object.ToolsStatus -eq "NotInstalled") {Add-Member -InputObject $object NoteProperty Version ""}
        else {Add-Member -InputObject $object NoteProperty Version $VM.Guest.ToolsVersion}
        Add-Member -InputObject $object NoteProperty "HW Version" $VM.config.version
        $report += $object
        $progress++
        }
    Write-Progress -Activity "Checking vmware tools" -Status "All done" -Completed -ErrorAction SilentlyContinue
 
    $report

Depending on the number of VMs in your environment, the list might now even fit on the screen. So you should probably pipe it to where-object to filter it down further, like this:

get-vmtoolsstatus-script

Posted in PowerCLI, Powershell, VMware Tagged , , , , ,

PowerCLI: Getting datastore alarms

Next in the series on getting alarms is getting datastore alarms.
Again, the code is pretty similar:

$Datastores = Get-View -ViewType Datastore -Property Name,OverallStatus,TriggeredAlarmstate
$FaultyDatastores = $Datastores | Where-Object {$_.TriggeredAlarmState -ne "{}"}
 
$progress = 1
$report = @()
if ($FaultyDatastores -ne $null) {
    foreach ($FaultyDatastore in $FaultyDatastores) {
        foreach ($TriggeredAlarm in $FaultyDatastore.TriggeredAlarmstate) {
            Write-Progress -Activity "Gathering alarms" -Status "Working on $($FaultyDatastore.Name)" -PercentComplete ($progress/$FaultyDatastores.count*100) -Id 1 -ErrorAction SilentlyContinue
            $entity = $TriggeredAlarm.Entity.ToString()
            $alarmID = $TriggeredAlarm.Alarm.ToString()
            $object = New-Object PSObject
            Add-Member -InputObject $object NoteProperty Datastore $FaultyDatastore.Name
            Add-Member -InputObject $object NoteProperty TriggeredAlarms ("$(Get-AlarmDefinition -Id $alarmID)")
            $report += $object
            }
        $progress++
        }
    }
Write-Progress -Activity "Gathering alarms" -Status "All done" -Completed -Id 1 -ErrorAction SilentlyContinue
 
$report | Where-Object {$_.TriggeredAlarms -ne ""}

And the output is pretty similar:

get-datastorealarms

The function code is this:

function Get-DatastoreAlarms
{
$Datastores = Get-View -ViewType Datastore -Property Name,OverallStatus,TriggeredAlarmstate
$FaultyDatastores = $Datastores | Where-Object {$_.TriggeredAlarmState -ne "{}"}
 
$progress = 1
$report = @()
if ($FaultyDatastores -ne $null) {
    foreach ($FaultyDatastore in $FaultyDatastores) {
        foreach ($TriggeredAlarm in $FaultyDatastore.TriggeredAlarmstate) {
            Write-Progress -Activity "Gathering alarms" -Status "Working on $($FaultyDatastore.Name)" -PercentComplete ($progress/$FaultyDatastores.count*100) -Id 1 -ErrorAction SilentlyContinue
            $entity = $TriggeredAlarm.Entity.ToString()
            $alarmID = $TriggeredAlarm.Alarm.ToString()
            $object = New-Object PSObject
            Add-Member -InputObject $object NoteProperty Datastore $FaultyDatastore.Name
            Add-Member -InputObject $object NoteProperty TriggeredAlarms ("$(Get-AlarmDefinition -Id $alarmID)")
            $report += $object
            }
        $progress++
        }
    }
Write-Progress -Activity "Gathering alarms" -Status "All done" -Completed -Id 1 -ErrorAction SilentlyContinue
 
$report | Where-Object {$_.TriggeredAlarms -ne ""}
 
<#
 .Synopsis
  Lists all triggered Cluster alarms that haven't been acknowledged
 .Description
  Outputs a list of Cluster and the unacknowledged alarms they have triggered
 .Example
  Get-VMHostAlarms
  Outputs a list of Cluster and the unacknowledged alarms they have triggered
 .Link
  http://cloud.kemta.net
 #>
}

 

Posted in PowerCLI, Powershell, VMware Tagged , , , , ,