ViewCLI | Find users & assigned VM that did not login for more than 90 days

I didn’t find this anywhere else so i decided to create my own ViewCLI script.

Just run it against a View Event Database to get all information you need.

Please note:

If there is more than one machine assigned to a user it will show “System.Object[]” instead of a machine name.

You can still use “get-desktopVM | where { $_.user_displayname -eq “domain\username” } to list all machines in that case.

It actually results users that did login to the broker. This is enough for me as i don’t need the agent_login itself.

Just make sure you edit the sql query if you want to see agent connects.

Let me know if you have any questions or problems:

 

<#
.SYNOPSIS
Check environment for users login times and get machines with no login since more than 90days.
#>

[CmdletBinding()]
param(
    [Parameter(Mandatory=$False)]
    [string]$choice
)

#generate a timestamp that can be used in filename
Function logstamp {
$now=get-Date
$yr=$now.Year.ToString()
$mo=$now.Month.ToString()
$dy=$now.Day.ToString()
$hr=$now.Hour.ToString()
$mi=$now.Minute.ToString()
if ($mo.length -lt 2) {
$mo="0"+$mo #pad single digit months with leading zero
}
if ($dy.length -lt 2) {
$dy="0"+$dy #pad single digit day with leading zero
}
if ($hr.length -lt 2) {
$hr="0"+$hr #pad single digit hour with leading zero
}
if ($mi.length -lt 2) {
$mi="0"+$mi #pad single digit minute with leading zero
}
Write-Output $yr$mo$dy$hr$mi
}

#variables - modify accordingly
$dbserver = ""
$user = ""
$pwd = read-host 'View Event DB Password' -AsSecureString
$database = "VIEWEVENTS"
$connectionString = "Server=$dbserver;uid=$user; pwd=$pwd;Database=$database;Integrated Security=False;"
$domain = ""
#this query actually gets all users that did not login between today and -90 days but before -90 days until -180 days.
#you could choose to increase or decrease the -180 as you want.
$query = 
"
SELECT ModuleAndEventText 
FROM VE_event_historical 
WHERE (EventType = 'BROKER_USERLOGGEDIN') AND (Time BETWEEN dateadd(day,-180,getdate()) AND dateadd(day,-90,getdate())) 
EXCEPT 
SELECT ModuleAndEventText 
FROM VE_event_historical 
WHERE (EventType = 'BROKER_USERLOGGEDIN') AND (Time BETWEEN dateadd(day,-90,getdate()) AND dateadd(day,0,getdate()))
"

$connection = New-Object System.Data.SqlClient.SqlConnection
$connection.ConnectionString = $connectionString
$connection.Open()
$command = $connection.CreateCommand()
$command.CommandText  = $query
$result = $command.ExecuteReader()
$table = new-object “System.Data.DataTable”
$table.Load($result)
$connection.Close()

$properties = @{UserID = ''; VM=''}
$object = New-Object -TypeName PSObject -Property $properties

$collection=@()

$table | foreach-object {
    $objloop = $object.PSObject.Copy()
    #make sure to edit the 2nd -replace to match your domain
    $objloop.UserID = $_.ModuleAndEventText -replace " has logged in", "" -replace "User domain\\", ""
    $objloop.VM = get-desktopvm | where { $_.user_displayname -eq "$domain\$($objloop.UserID)" } | Select-Object -exp Name
    $collection += $objloop
    }

write-output "Logging output to InactiveDesktops_$timestamp.csv"

$timestamp = logstamp
$filename = "InactiveDesktops_$timestamp.csv"
$collection | Export-Csv $filename -NoTypeInformation

 

 

Powershell Script | Template (input from pipeline / error handling)

If you want to have a template that delivers some basic functionality just use this as and modify it to your needs.

It needs to have an input but also accepts input from a pipeline (like get-content input.txt | script.ps1).

It offers basic error handling for terminating errors. The variable $ErrorActionPreference = stop makes them all terminating though 🙂

 

Have fun and let me know if you need any advice.

<#
.SYNOPSIS
Check if VM(s) still exist
.EXAMPLE
get-content "list-of-machines.txt" | check if vm exists.ps1
.EXAMPLE
check if vm exists.ps1 vm1
.EXAMPLE
check if vm exists.ps1 vm1, vm2, vm3
.PARAMETER VM
One or more Virtual Machine names
#>

[CmdletBinding()]
param(
    [Parameter(Mandatory=$True,ValueFromPipeline=$True)]
    [string[]]$VM
)

BEGIN {
    $ErrorActionPreference = "Stop"
    $collection = @()
}

PROCESS {
    try {
        $collection += get-vm $desktop
        write-output "$desktop does exist!"
        }
    catch {
        $collection += "$desktop does not exist!"
        }
    finally {}
}

END {
    $collection | out-gridview
}

 

PowerCLI Script | Find orphaned VMDK & Snapshots

If find this script really useful for finding orphaned VMDKs.

Just put it as task once a week and you will be able to cleanup nicely 🙂

Make sure to change the variables on the top of the script to fit your needs.

 

#########################
# BEGIN: User Variables #
#########################

# Log File location
$LogFile = "Snapshot.log"

# Virtual Centre server to connect to
$VCServer = ""

# Email parameters for emailing logfile results
# emailTo is a comma separated list of strings eg. "email1","email2"
$emailEnable = $true
$emailFrom = "vcs"
$emailTo = "vcs"
$emailSubject = "VMware Health Check"
$emailServer = ""

#########################
#   END: User Variables #
#########################

###############################
# BEGIN: Function Definitions #
###############################

function Output-Data
{
<#
.SYNOPSIS       Outputs Data or messages in the desired method
.DESCRIPTION    This function is designed to use the $LogFile global variable to avoid
                having to specify the output file each time it is called.
                Data is output to the log file by default and optionally to the console as well.
.NOTES          Author:  Grant Brunton
.PARAMETER      Data:
                    The message or object to ouput to the log
.PARAMETER      ToHost:
                TH:
                    Optional switch to include displaying the output to the console
.EXAMPLE
                PS> Output-Data $object
.EXAMPLE
                PS> Output-Data "Message" -ToHost
#>

    [CmdletBinding()]
    Param
    (
        [parameter(Mandatory=$true, ValueFromPipeline=$true, Position=0)]
        [Object]$Data,
        [alias("TH")]
        [switch]$ToHost
    )
    
    Process
    {
        if ($ToHost) { $Data | fl }
        $Data | fl | Out-File $LogFile -Encoding ASCII -Append
    }
}

function Load-VMLibrary
{
<#
.SYNOPSIS       Loads VMware core modules and connects to VI Server
.DESCRIPTION    The function loads the VMware core modules required
                for processing VMware PowerCLI commands.
                This requires the PowerCLI modules to be installed.
                It will also connect to the VIServer ready for accepting commands.
.NOTES          Author:  Grant Brunton
.PARAMETER      VIServer:
                    The Virtual Centre Server to connect to.
                    The default is "VirtualCentre"
.PARAMETER      Credential:
                    A PSCredential object used to authenticate with the VIServer to connect to.
                    Credential objects can be created using the Get-Credential cmdlet
                    By default the logged on user credentials are used to authenticate.
.EXAMPLE
                PS> Load-VMLibrary
.EXAMPLE
                PS> Load-VMLibrary "server1"
.EXAMPLE
                PS> $cred = Get-Credential
                PS> Load-VMLibrary -VIServer "server1" -Credential $cred
.EXAMPLE
                PS> $cred = $host.ui.PromptForCredential("ESX/ESXi Credentials Required", "Please enter credentials to log into the ESX/ESXi host.", "", "")
                PS> "server1" | Load-VMLibrary -Credential $cred
#>

    [CmdletBinding()]
    Param
    (
        [parameter(ValueFromPipeline=$true,Position=0)]
        [String]$VIServer = "VirtualCentre",
        [object]$Credential = $null
        
    )
    
    Process
    {
        $vmwaretoolkit = Get-PSSnapin | where {$_.Name -eq "VMware.VimAutomation.Core"}

        if (!$vmwaretoolkit)
        {
            $vmwaretoolkit = Get-PSSnapin -registered | where {$_.Name -eq "VMware.VimAutomation.Core"}
            if ($vmwaretoolkit)
            {
                Add-PSSnapin "VMware.VimAutomation.Core"
                if (!$?) { Output-Data -TH "Failed to load VMware snapin. Ensure VMware vSphere PowerCLI is installed correctly." ; exit }
            }
            else
            {
                Output-Data -TH "Please install VMware vSphere PowerCLI to use this script."
                exit
            }
        }

        Set-PowerCLIConfiguration -DefaultVIServerMode Single -Confirm:$false > $null
        if ($Credential -ne $null)
        {
            if ($Credential.GetType().Name -ne "PSCredential")
            {
                Output-Data -TH "Invalid credential format supplied"
                exit
            }
            $VIServer = Connect-VIServer $VIServer -Credential $Credential
        }
        else
        {
            $VIServer = Connect-VIServer $VIServer
        }
        if (!$?) { Output-Data -TH "Failed to connect to VM host. Please ensure the correct VIServer is specified and you have correct logon credentials." ; exit }
    }
}

function Check-OrphanedData{
<#
.SYNOPSIS   Remove orphaned folders and VMDK files
.DESCRIPTION   The function searches orphaned folders and VMDK files
   on one or more datastores and reports its findings.
   Optionally the function removes  the orphaned folders   and VMDK files
.NOTES   Author:  Luc Dekens
         Modified by:  Grant Brunton
.PARAMETER Datastore
   One or more datastores.
   The default is to investigate all shared VMFS datastores
.PARAMETER Delete
   A switch that indicates if you want to remove the folders
   and VMDK files
.EXAMPLE
   PS> Remove-OrphanedData
.EXAMPLE
  PS> Get-Datastore ds* | Remove-OrphanedData
.EXAMPLE
  PS> Remove-OrphanedData -Datastore $ds -Delete
#>

  [CmdletBinding()]
  param(
      [parameter(ValueFromPipeline=$true)]
      [PSObject[]]$Datastore,
      [switch]$Delete
  )

  begin{
    $fldList = @{}
    $hdList = @{}

    $fileMgr = Get-View FileManager
  }

  process{
    if(!$Datastore){
      $Datastore = Get-Datastore
    }
    foreach($ds in $Datastore){
      if($ds.GetType().Name -eq "String"){
        $ds = Get-Datastore -Name $ds
      }
      if($ds.Type -eq "VMFS" -and $ds.ExtensionData.Summary.MultipleHostAccess){
        Get-VM -Datastore $ds | %{
          $_.Extensiondata.LayoutEx.File | where{"diskDescriptor","diskExtent" -contains $_.Type} | %{
            $fldList[$_.Name.Split('/')[0]] = $_.Name
            $hdList[$_.Name] = $_.Name
          }
        }
        Get-Template | where {$_.DatastoreIdList -contains $ds.Id} | %{
          $_.Extensiondata.LayoutEx.File | where{"diskDescriptor","diskExtent" -contains $_.Type} | %{
            $fldList[$_.Name.Split('/')[0]] = $_.Name
            $hdList[$_.Name] = $_.Name
          }
        }

        $dc = $ds.Datacenter.Extensiondata

        $flags = New-Object VMware.Vim.FileQueryFlags
        $flags.FileSize = $true
        $flags.FileType = $true

        $disk = New-Object VMware.Vim.VmDiskFileQuery
        $disk.details = New-Object VMware.Vim.VmDiskFileQueryFlags
        $disk.details.capacityKb = $true
        $disk.details.diskExtents = $true
        $disk.details.diskType = $true
        $disk.details.thin = $true

        $searchSpec = New-Object VMware.Vim.HostDatastoreBrowserSearchSpec
        $searchSpec.details = $flags
        $searchSpec.Query += $disk
        $searchSpec.sortFoldersFirst = $true

        $dsBrowser = Get-View $ds.ExtensionData.browser
        $rootPath = "[" + $ds.Name + "]"
        $searchResult = $dsBrowser.SearchDatastoreSubFolders($rootPath, $searchSpec)
        foreach($folder in $searchResult){
          if($fldList.ContainsKey($folder.FolderPath.TrimEnd('/'))){
            foreach ($file in $folder.File){
              if(!$hdList.ContainsKey($folder.FolderPath + $file.Path)){
                $obj = New-Object PSObject -Property @{
                  Datastore = $ds.Name
                  Folder = $folder.FolderPath
                  FileName = $file.Path
                  Size = $file.FileSize
                  #CapacityKB = $file.CapacityKb
                  #Thin = $file.Thin
                  #Extents = [string]::Join(',',($file.DiskExtents | %{$_}))
                  Problem = "Orphaned file"
                }
                Output-data $obj
                if($Delete){
                  $dsBrowser.DeleteFile($folder.FolderPath + $file.Path)
                }
              }
            }
          }
          elseif($folder.File | where {"cos.vmdk","esxconsole.vmdk" -notcontains $_.Path}){
            $obj = New-Object PSObject -Property @{
              Datastore = $ds.Name
              Folder = $folder.FolderPath
              Problem = "Orphaned folder"
            }
            Output-data $obj
            if($Delete){
              $fileMgr.DeleteDatastoreFile($folder.FolderPath,$dc.MoRef)
            }
          }
        }
      }
    }
  }
}

function Check-Snapshot
{
<#
.SYNOPSIS       Checks VM guests for invalid snapshot images
.DESCRIPTION    This function checks VM guests to see if their harddisks are pointing to snapshot files.
                If they are it reports a detected problem if there are no snapshots listed for the guest
                or if a Consolidate Helper snapshot exists for the guest.
                A Consolidate Helper snapshot is usually created by a VCB type backup process and can be left behind
                if a snapshot removal process failed or the datastore ran out of room.
                If the Consolidate Helper snapshot appears by itself this is an indicator of a failed process.
                If the Consolidate Helper exists with other snapshots it may still be in the middle of the
                removal process.
.NOTES          Author:  Grant Brunton
.PARAMETER      VMGuest:
                    Can be an array or a single VM guest to check.
                    Input should be either the VM object or a string of the VM name.
                    By default all VM guests are checked.
.EXAMPLE
                PS> Check-Snapshot
.EXAMPLE
                PS> Check-Snapshot [-VMGuest] "vmguest"
.EXAMPLE
                PS> $vm = Get-VM "vmguest"
                PS> $vm | Check-Snapshot
#>

    [CmdletBinding()]
    Param
    (
        [parameter(ValueFromPipeline=$true, Position=0)]
        [PSObject]$VMGuest
    )
    
    Process
    {
        if(!$VMGuest)
        {
          $VMGuest = Get-VM
        }
        
        foreach($vm in $VMGuest)
        {
            if($vm.GetType().Name -eq "String")
            {
                $vm = Get-VM -Name $vm
            }
        	
            $vm | Get-HardDisk | %{
        		if ($_.Filename -match ".*-[0-9]{6}.vmdk")
                {
                    $obj = $null
                    if (!(Get-Snapshot $vm))
                    {
                        $obj = New-Object PSObject -Property @{
                          VMName = $vm.Name
                          VMHost = $vm.Host
                          Problem = "Missing Snapshot from Snapshot Manager"
                        }
                    }
                    elseif (Get-Snapshot $vm | where{$_.Name -like "Consolidate*"})
                    {
                        if (@(Get-Snapshot $vm).Length -eq 1)
                        {
                            $obj = New-Object PSObject -Property @{
                              VMName = $vm.Name
                              VMHost = $vm.Host
                              Problem = "Consolidate Helper exists for VM with no snapshots"
                            }
                        } else {
                            $obj = New-Object PSObject -Property @{
                              VMName = $vm.Name
                              VMHost = $vm.Host
                              Problem = "Consolidate Helper exists for VM but has snapshots"
                            }
                        }
                    }
                    
                    if ($obj -ne $null) { Output-Data $obj }
                    Continue
                }
            }
        }
    }
}

###############################
#   END: Function Definitions #
###############################

####################
# BEGIN: MAIN CODE #
####################

if (Test-Path $LogFile) { del $LogFile }
Load-VMLibrary $VCServer

Check-Snapshot
Check-OrphanedData

if ($emailEnable -and (Test-Path $LogFile))
{
    Send-MailMessage -smtpserver $emailServer -to $emailTo -from $emailFrom -subject $emailSubject -body (Get-Content $LogFile | Out-String)
}

 

VMWare Tools Installation | Network resource unavailable (error1316)

If you ever encounter the problem that you can’t update or install vmware tools (uninstall is also affected) due to missing soure.
Try this KB from VMWare.

VMware Tools may not install on a Windows guest operating system after upgrading to a newer version of ESX/ESXi (1012693)

Symptoms

  • VMware Tools does not install on a Windows 2008/2003 guest operating system after upgrading to a newer version of ESX/ESXi.
  • Automatic installation of VMware Tools fails.
  • Interactive installation of the VMware Tools tries to uninstall the previous version, but it fails because it cannot reference the previous VMware Tools MSI.
  • You may receive errors similar to:
Error1316 A network error occurred while attempting to read from the file C:\Windows\Installer\VMware Tools.msi.
Error 1706. Setup cannot find the required files.The installation source for this resource is not available. Verify that the source exists and that you can access it.

Cause

This is an issue with Microsoft Windows Installer and does not affect all Windows 2008/2003 systems. The problem can occur when the original install path from the older version of VMware Tools is invalid, such as if the install path was E:\ and that path is no longer present. For more information, see the Microsoft Knowledge Base article 555175.
Note: The preceding link was correct as of June 21, 2013. If you find the link is broken, provide feedback and a VMware employee will update the link.

Resolution

To resolve this issue, you must perform a forced uninstall and reinstall of VMware Tools.
To uninstall and reinstall VMware Tools:
  1. Right-click the virtual machine and click Guest > Install/Upgrade VMware Tools.
  2. Open a Console to the virtual machine and log into the guest operating system.
  3. Click Start > Run, type cmd, and click OK. A Command Prompt window opens.
  4. Change the drive to your CD-ROM drive (For example, D:\).
  5. Type setup /c and press Enter to force removal of all registry entries and delete the old version of VMware Tools.Note: For 64-bit guest operating systems, type setup64 /c
  6. Open My Computer and double-click the CD-ROM that contains VMware Tools.
  7. After Auto-Run starts, follow the prompts to install.Note: This must be done from the GUI interface. Do not launch the install by running Setup from the Command Prompt. Also ensure that the CD-ROM is enabled. In the virtual machine properties select CD-ROM under Device Status, and ensure that Connected (if the virtual machine is powered on) and Connect at power on are selected.
  8. When the installation completes, reboot the guest operating system.

View Agent | The JVM has aborted

On View desktops that have HP QuickTest Professional installed, View Agent fails (2036437)

Details

When HP QuickTest Professional software is installed on a View desktop, View Agent stops running and cannot communicate with View Connection Server. As a result, users cannot access the View desktop.
In the view agent logs, you see entries similar to:

T09:50:47.588-05:00 WARN (0870-0874) <Main Thread> [] Error occurred during initialization of VM
T09:50:47.588-05:00 WARN (0870-0874) <Main Thread> [] Could not find agent library jvmhook on the library path, with error: Can’t load IA 32-bit .dll on a AMD 64-bit platform
T09:50:47.588-05:00 WARN (0870-0874) <Main Thread> []
T09:50:47.588-05:00 FATAL (0870-0874) <Main Thread> [] The JVM has aborted.
T09:50:47.744-05:00 INFO (03E0-0838) <JavaBridge> [wsnm_jmsbridge] wsnm_jms died, restarting in a minute

Note: For more information on View Agent log locations, see Location of VMware View log files (1027744).

This issue occurs because HP QuickTest Professional sets the following environment variables on the desktop virtual machine:

‘_JAVA_OPTIONS’
‘JAVA_TOOL_OPTIONS’
When View Agent loads the jvm.dll, it attempts to load the libraries specified by these environment variables. When View Agent cannot load the specified libraries, it fails.

Solution

After installing HP QuickTest Professional, remove the following environment variables from the virtual machine that you will use to create desktop pools:
‘_JAVA_OPTIONS’
‘JAVA_TOOL_OPTIONS’

After removing these environment variables, install View Agent.

Win7/Vista – MSI Installer Safe Mode

Referenced from: http://www.symantec.com/connect/blogs/windows-installer-safe-mode

Windows Installer will not work under Safe Mode, this means that programs cannot be installed or uninstalled in safe mode without giving a specific command using msiexec in command prompt.

To make Windows Installer work under safe mode, you need to create a registry entry for every type of safe mode you are logged in to.

  1. Safe Mode.
    Type this in a command prompt:

    REG ADD "HKLM\SYSTEM\CurrentControlSet\Control\SafeBoot\Minimal\MSIServer" /VE /T REG_SZ /F /D "Service"
    

    and then

    net start msiserver
    
    

    This will start the Windows Installer Service.

  2. Safe Mode with Network
    REG ADD "HKLM\SYSTEM\CurrentControlSet\Control\SafeBoot\Network\MSIServer" /VE /T REG_SZ /F /D "Service"
    
    

    and followed by

    net start msiserver 
    
    

    This will start the Windows Installer Service.

Win7 / Vista – Registry access denied

If you ever happen to be in the same position as me and your registry doesn’t let you change keys/values, check this article as it resolved my issue.

http://www.smartestcomputing.us.com/topic/51422-vistaseven-the-registry-editor-could-not-set-security-in-the-key-currently-selected-or-some-of-its-subkeys-error-fix/

 

—-

Download PsExec.exe to your desktop (IMPORTANT!)
Go Start and in “Start search” type in:
cmd
Hold CTRL and SHIFT keys, press Enter.
Command prompt window will open.
Copy and paste following command:

“%userprofile%\desktop\psexec” -i -d -s c:\windows\regedit.exe

Press Enter.
Registry Editor will open.

Now you’ll be able to change permissions for any key.

—-

Free WiFi Düsseldorf

Did you know about the 11 current free WiFi spots in Düsseldorf?
Here you go:

Königsallee, Ecke Grünstraße
Königsallee, hinter Bahnstraße
Königsallee, Ecke Steinstraße
Königsallee, Ecke Graf-Adolf-Straße
Schadowstraße, Ecke Berliner Allee
Martin-Luther-Platz
Grabbeplatz, Ecke Neubrückstraße
Hunsrückenstraße, Ecke Flinger Straße
Mühlenstraße auf dem Burgplatz
Mittelstraße, Ecke Carlsplatz
Hammer Straße, Ecke Franziusstraße

Just select the “bluespot” WiFi – it’s free and unlimited (bandwidth/volume).
Enjoy!

Site layout

I just changed some margins to lower values as i had the impression there was too much space in between the elements. Let me know what you think of it.